I have encountered many engineers who are excited about Go and want to adopt it for enterprise systems. In 2019. I evaluated Go for that purpose. You can read my evaluation at http://glennengstrand.info/software/architecture/microservic...
What I discovered is that Go is not any faster than Java on a light weight framework. Here is the part that may seem controversial for the folks here. The simplicity of Go did not result in simpler systems.
I found that the lack of inheritance hampered me in expressing solutions to complex business rules and data models. Many of you may disagree with that and counter argue that inheritance is a bad thing. That is most probably one reason why there is so much interest in Go. It intentionally lacks support for inheritance. I agree that inheritance can and has been misused a lot. It is an advanced programming language feature that should be used sparingly. Removing inheritance entirely reduces the expressiveness of the programming language, especially when it comes to enterprise computing.
Inheritance is the number one cause of these turning into a massive shit show. It’s not a pattern that can ever be refactored into something else down the line once the decision is made. You end up with things that can’t be fixed and escalating costs like you have never seen just to keep the plates spinning.
I always favour composition for that and very light weight service oriented architecture.
Roughly the only thing that scales is things that have fairly strict command-query type segregation. Carefully designed REST interfaces are a good example of that.
Regarding complex business rules they are mostly best evaluated through simple stacked middleware, messaging, matching, routing and workflow style applications. All of those are feasible with the simplest of languages. Hell half the world is still hanging off rancid bits of COBOL.
If you are short circuiting your evaluation on this basis I think you are making a lot of noise without a lot of experience.
Personally speaking, choosing between languages is a non-starter conversation for me. At this point, since my childhood, I have rotated through 10+ languages. Languages are certainly tools and picking the right tool for the job is important. That said, I don't want to work at another company that mandates one language (as is commonly the case with Java) across the entire enterprise. We need solutions that bridge tooling language gaps for enterprises more than we need archaic mandates about what I'm allowed to write in for a given purpose.
Managing a bunch of polymorphic entities in Go often requires implementing a bunch of interfaces in various types. You then have to write a bunch of mapping logic for every type, driven by type assertions. Apply this in a large codebase that encapsulates transport, data access, and so on and you end up with a ton of complexity.
I echo this 100%. I worked at an employer who heavily used golang. The resulting projects were honestly a mess, yet they pushed through.
What's ironic, is that they ended up reinventing the wheel on so many different things, including DI and an entire application framework. Millions of dollars spent writing and maintaining these libraries, where Java already had them ages ago.
As you point out, the modeling capability of golang is extremely subpar. It results in verbose code that is very sparse, lots of code to implement something that would have taken a few lines in a language like Java and C#, let alone something more dense like Scala.
The way interfaces are handled in golang makes it very annoying to try to find out which types implement said interface. It puts a lot of pressure on the IDE to search the entire code base, and you end up with types you don't even care about. It clearly shows that the golang authors did not have IDEs in mind when writing the language, which is just absurd as it's supposedly a language designed for "programming in the large". Anyone who used "goland" knows what I'm talking about. Try refactoring a type, and have the IDE scan the entire code base to look for comment strings, which it has to because golang has no notion of doc strings like Java or C#.
Add features like records, pattern matching, enums (the amount of code that had to be written to implement something to emulate enums was just absurd and frustrating to deal with, not to mention error prone).
The concurrency aspect of golang is decent (though it will be even better in Java). Other than that, the language doesn't really have anything going for it other than having a large brand name backing it.
Thing is none of the above is going to apply when generics land. You mentioned kubernetes which use complex, maintainable abstractions and structures with only the facilities Go provides. There isn't that much difference between writing a maintainable system, whether the primary user is the business, or other software.
That said, can someone weigh in on how it is building large "enterprise-grade" applications in Go 1.x given how (purposefully) constrained the type system in Go is? While protocols in Go are great (one of the very many breaths of fresh air that Go provided), operations on data structures without generics does not seem like a good time. Does everyone get around the lack of generics by making most generic-looking operations slightly-specialized structs and some interface composition?
That is, in places where "product understands the problem it wants to solve, and wants to engage with dev about it", and the engineering manager cared about the problem, and worked to get the team to understand and care about it, pitch potential solutions, and then deliver them in a prioritized manner, DDD wasn't even suggested. It just wasn't necessary; all the useful artifacts from it happened naturally because product and dev were so closely aligned.
In the places where product didn't really know what problem they wanted to solve, and didn't want to engage with dev, and the org pressures were around documentation and gating, DDD was heralded as the cure-all that would get everything flowing smoothly, while solving many of the documentation and gating needs. It didn't do any of that.
It's very hard to justify why teams within an enterprise should not use Java/SpringBoot. A typical enterprise already has thousands of developers who support and develop existing critical business applications. Due to historical reasons most of these developers are familiar with Java and they find it hard to justify using another language.
That coupled with the fact that Java and SpringBoot made some really good choices and can compete with most newer language features, its hard for other languages to penetrate that.
I'm not a Go developer, but as a C# developer I can say that a more powerful type system, generics etc can be both a boon and a curse - especially so for large enterprise systems.
I've seen these go both ways, and it ultimately comes down to two things:
1. being able to create a design with the right abstractions more or less from the start 2. not over-using generics and inheritance
If you don't succeed with these, or/and you don't forsee some things you end up needing later, the code becomes an incomprehensible mess, as developers try to crowbar functionality into the constraints of the design, or make things even more complicated by refactoring and introducing even more use of generics and inheritance.
A lot of the problem emerges if you try to use some organizational pattern that, without you perhaps realizing it, is foundationally based on being able to have circular dependencies, or otherwise working poorly with Go's visibility rules.
I have found myself becoming increasingly disenchanted with all the heavyweight design methodologies over the years anyhow. They amount to a claim: "All problems you may ever face have this structure and as a result this is the optimal structure to meet those problems with code." The problem is that first bit is wrong. Problems have all kinds of different structures. Start with the layered approach and your code will naturally follow the faultlines of the problem, whatever it may be. Try to jam the code into a particular structure up front and you just have to hope that it is the right one; you won't know until you've put a lot of work in, and, almost certainly for any non-trivial project, there will be at least one major component for which it is actually wrong. (And often it's just wrong across the board.)
Worse, people who get too focused on a particular up-front highly-structured design lose the ability to even realize that their structure is the wrong structure; it starts becoming the very lens through which they see the world.
If you back all the way out to the theoretical/academic realm and view the problem domain through the lens of normalization, you would probably find that any opportunity for use of generics to be a sign that you have not achieved 3NF.
Our core domain models are extremely simple types. They have no other complex types as properties and could be mapped 1:1 to SQL tables without any complication.
We could have certainly leveraged generics if it made sense (we use C#). But, for these core domain model types it causes more harm than good.
"Microservices" is a business setting is somewhat laughable. After a number of years, yes, I have a handful of services that run outside of the main program. But the Line of Business core application might as well be a single executable.
Most of the time once you are getting to business-oriented software I think you tend to have more concrete implementations and you don't miss generics much (other than the implementation side where there is the pain of using libraries that didn't have access to them)
I think this is largely correct. It's pretty rare that I find myself wishing for generics; rather, I find myself frequently wishing for sum types (Go has workarounds but they tend to be laborious to express, keep updated, etc and they still make weaker guarantees wrt exhaustivity).
That said, most of the complaints I hear about generics are about terseness rather than correctness (i.e., people want to elide lots of little bits of boilerplate), which is perhaps why I don't find myself missing generics very much--I'm happy to put up with verbosity as long as correctness and readability are preserved.
When it comes to domain code - probably when you are starting to think about generics that's a bad sign. Probably you are trying to make it overcomplicated. Domain code should stay simple and interfaces should be enough to handle it.
But when it comes to libraries the situation is totally different. One of the example is https://watermill.io library (that we are authors of BTW). Generics could give some nice simplifications.
It's also a case for event-sourcing library that we are using in the company where we are working on now. But You can still do a lot with interface{} and reflection. But it's not perfect and generate a lot of boilerplate.
The third example is a decorator pattern. You can't create generic decorator without code generation. It is of course some sort of the solution, but it's not trivial to implement.
Define the needs of the application, develop a framework to meet those needs, then define the application into being. Never write a business application screen by screen; define it into being screen by screen.
Feels like a clickbait, especially after your learn that the consultancy that wrote the book built their whole identity around being "DDD in Go" experts. Just don't push it on everyone. This kind of marketing is doing real harm to Go newbies, like that "standard Go project template" repo.
https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-exampl...
First: DDD will not magically fix all your problems; however, it does ask the right questions: what is your common language, how does your code align with your organisation, what are the boundaries and the relationship between boundaries, what is your core domain and where might it make more sense to buy something existing etc... So while it is not the silver bullet, it might help you to pinpoint some typical issues with your current code base.
Second, people new to DDD tend to focus on tactical patterns and things like CQRS etc, which might be interesting but it's usually way less important than the strategic part of DDD.
Third, it also attempts to define nomenclature for developers, f.e. about responsibilities and granularities: aggregate roots, bounded contexts, commands, events etc...
Like every other methodology there is some cargo culting going on, and that is also the reason I decided to spend a few years outside the echo chamber. However, now that I'm back it's great to see how the methodology is maturing and the community trends to attract the right kind of people.
TL;DR: DDD can be useful, but don't blindly start applying tactical DDD patterns everywhere without understanding the bigger picture; this will only work against you.
Edit: some line breaks
Hey, one little tip. I see you're using LaTeX for the book. If you switch to xelatex to compile it, you can use your own fonts, like this:
% Set the typeface to use
\usepackage{fontspec}
\setmainfont{Libre Baskerville}
And the compile command: xelatex mybook.texWhy does the domain-driven-design chapter come after you’ve tied the reader into GCP? I can’t think of a single enterprise business domain where that would make sense from a European GDPR driven perspective.
Why do you think you can cover that many topics in a single book? I mean, part of the reason Clean Code is well liked is because it covers one topic in depth, rather than covering a bunch of different topics without ever giving the reader anything of value on any of them.
Have you considered going the TDD GOAT book route of earning sales?
I’m really sorry that I’m so negative, but your “contents” read like every terrible tech-book I’ve ever opened. Good luck though.
> Aren’t the first 3 or 4 chapters basically what you can find in Google’s own cloud toturials? > Why does the domain-driven-design chapter come after you’ve tied the reader into GCP? I can’t think of a single enterprise business domain where that would make sense from a European GDPR driven perspective.
Our idea was to create a seemingly modern app based on microservices, with full GCP setup and fancy tools used. We then go on to point the issues in the code. We wanted to show how an app that seems well built on surface can have hidden problems that are hard to spot. We don't deep dive into GCP, mostly just describe the setup.
> Why do you think you can cover that many topics in a single book? I mean, part of the reason Clean Code is well liked is because it covers one topic in depth, rather than covering a bunch of different topics without ever giving the reader anything of value on any of them.
I believe you'll find a lot of value on these topics, even if we don't go super-deep into each of them. It should give readers enough ideas on how to approach building complex apps. We base this on our experience, so it's not just bland descriptions of the patterns. Also it's all focused on Go and on a real example project, so it's not abstract.
I'll add information to subscribe confirmation page about the delay.