10 ms·
So I think DDD is basically one of the best pattern that has ever been discovered for developing good software (thinking of it along with gangof4, SOLID, etc).
by hardwaresofton 5y ago
So I think DDD is basically one of the best pattern that has ever been discovered for developing good software (thinking of it along with gangof4, SOLID, etc). There's a lot of cruft around it and consultants looking to over-complicate it for the sake of getting hired, but viewed simply it's mostly about abstraction at conceptual boundaries -- very hard to be against that on most large projects.
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?
- llimllib 5y agoI built a large enterprise website in go, and I can say that I barely ever even had to consider the lack of generics. Occasional slightly annoying interface shenanigans and mild interface hackery, but nothing significant at all.
- hardwaresofton 5y agoThanks for mentioning this experience -- I was really excited about Go early on but went with other languages after a while so didn't make any huge projects with it (and definitely not at a large employer/go shop). Did you have to do a lot of stuff that is DDD-related? Like building out abstractions for adapters and connection points, or did you use libraries that did that part for you mostly? I know of some stuff out there like go kit[0] which is quite pragmatic and does some of the 80% use-cases (serialization, transports, etc) DDD stuff for you. I think for the most part it's rare to actually need to write a lot of your own DDD pieces for CRUD-y apps, and the parts where the complexity would be worth it are often already done for you by the libraries/frameworks used. [0]: https://gokit.io/ https://gokit.io/
- llimllib 5y agoI’m not the authors and don’t even really know what ddd means, sorry
- hardwaresofton 5y agoNo problem -- "DDD" refers to Domain Driven Design[0] [0]: https://en.wikipedia.org/wiki/Domain-driven_design https://en.wikipedia.org/wiki/Domain-driven_design
- m110 5y agoI think you mix some things up. go-kit is very far away from DDD. It even says on their website: "Focus on your business logic.". It's basically everything but not the domain code. For CRUD-y apps, you usually wouldn't need the DDD patterns at all, as there's no complex business logic to model. > the parts where the complexity would be worth it are often already done for you by the libraries/frameworks used. I'm really confused about this. The most complexity comes from complex business scenarios you need to handle somehow in code. No framework is flexible enough to do it for you. Using DDD, you would keep a "pure" layer of domain code that does just that.
- hardwaresofton 5y ago> I think you mix some things up. go-kit is very far away from DDD. It even says on their website: "Focus on your business logic.". It's basically everything but not the domain code. Paradoxically, I think you might actually be mixing some stuff up -- the only reason you can "focus on your business logic" is because the stuff at the edges is taken for you. DDD is the way of thinking that strives to let people focus on business logic by separating and abstracting the non-business layers. If you look for "ddd onion" you'll see the usual images of how this works. Go kit is valuable because it adheres to this. For example: > Pluggable serialization and transport — not just JSON over HTTP This is basically what pragmatic DDD looks like. Worry about what you need to say (the business logic), not how it's said. > For CRUD-y apps, you usually wouldn't need the DDD patterns at all, as there's no complex business logic to model. Agreed on this -- this is why I asked whether the app was mostly CRUD-y or not. > I'm really confused about this. The most complexity comes from complex business scenarios you need to handle somehow in code. No framework is flexible enough to do it for you. Using DDD, you would keep a "pure" layer of domain code that does just that. Ahh, that quote was referring to a mostly CRUD-y app -- as in I was noting how how DDD would not be useful in a mostly CRUD app (as you've noted, it isn't) mostly because it's already done for you (ex. go kit).
- hajhatten 5y agoI've had the same experience building both big and small systems. It's a feature of go, not something that's missing imo.
- GordonS 5y ago> 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? 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.
- rob74 5y agoFunny that you mention generics and inheritance in the same sentence - with all the hand-wringing going on about the lack of generics in Go, I would have thought more people would also complain about the lack of inheritance, but that doesn't seem to be a thing - except for the people who don't consider Go object-oriented because it doesn't have inheritance, but that's another topic... But yeah, the design decisions taken for Go tend to lead to software that is easier to refactor, because you don't end up building an elaborate type system that boxes you in and is difficult to change when requirements change (as they always do).
- emodendroket 5y agoI have heard some co-workers complain but I think it is one of their best decisions.
- nicoburns 5y ago> with all the hand-wringing going on about the lack of generics in Go, I would have thought more people would also complain about the lack of inheritance, but that doesn't seem to be a thing Given that use of inheritance is typically discouraged even in languages that support it, that's hardly surprising. OTOH, generics are typically pervasive in languages that support them (and don't cause problems in the same way).
- kardianos 5y agoHave a large line of business application written in Go. The type system is just fine. Here is the key: stop writing CRUD structs and junk. Use a framework that understands your schema / data storage system. Declare what you need done, then let application definitions do the work. "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.
- gher-shyu3i 5y ago> Use a framework that understands your schema / data storage system. The whole supposed premise of golang is that it shuns frameworks. What frameworks do you recommend in golang for such applications? You're still going to end up writing mapping logic back and forth between your RPC layer, internal layer, and DTOs. It's extremely verbose and error prone in golang to manually write them.
- kardianos 5y agoI'm not suggesting you use a web-framework. If you write a business application, first have an application framework, specific to your application. And no, you won't have mapping logic between RPC and internal and DTO. Most people don't understand how to build an application. They think in terms of RPC and encoding, not in terms of Screens, Actions, and Workflows.
- gher-shyu3i 5y ago> first have an application framework, specific to your application. So we're reinventing the wheel each time no? > And no, you won't have mapping logic between RPC and internal and DTO. How would it work then? Use the same DTO across the board?
- sagichmal 5y ago> Most people don't understand how to build an application. They think in terms of RPC and encoding, not in terms of Screens, Actions, and Workflows. What are Screens, Actions, and Workflows?
- lostcolony 5y agoI'll be honest, I view DDD as an org smell at this point. Every place I've encountered it, it was attempting to fix with process something that needed to be fixed with culture. 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.
- camgunz 5y agoYeah I'll pile on here. DDD is a process fix, not a tech fix, and broadly unless you're using Java, you can't implement it very well. Or, you'll find that you're actually using it without knowing it. Case in point, we used Django at my last gig and Django basically is DDD (apps are domains, models are entities, etc). We then started implementing DDD and essentially built weirdly named Django on Django, with way worse performance--and now any engineer we hired had to know two things (Django _and_ DDD). I wouldn't necessarily blame DDD here though? Mostly the problems were we didn't have reasonable performance metrics. Our CTO thought we had a problem, then he decided we should implement DDD, then we implemented it, and he thought we no longer had a problem. But there were no numbers to point to, we didn't know if we were delivering features faster/slower, if our PRs were smaller/larger, if our defect rate was higher/lower. We _did_ know our app was slower, because it would timeout (ha), but that's kind of the worst way to find that out. But, generally my argument here is: Think of any baller software project you use. Is it VS Code? Chrome? GitHub? NGINX? Do any of these use DDD?
- jen20 5y agoDDD does not have a canonical implementation in the form you suggest - the modern consensus is that it is a problem space tool, not a solution space one. The meat of DDD is in the strategic design section, not the structural patterns which have clearly not evolved for the modern world. And no, none of those pieces of software listed use DDD, because none of them are business applications, and it would be very inappropriate for them to do so.
- Thaxll 5y agoAs if type systeme is necessary to build "enterprise-grade" software, "enterprise-grade" sounds to me like bloated Java with terrible framework like hibernate.
- hardwaresofton 5y agoIf you haven't tried a type system better than what Java used to be, you owe it to yourself to try one of the other options out there. Also, type systems are almost proven at this point to produce better and easier to maintain code in the long term. Startups may not need it while they're iterating super quick, but when they do that rewrite 1-3 years in or whatever, they'll be better served picking a language with a good type system. [EDIT] - Oh also, ORMs are bad, just don't use em unless you really need the velocity. Hooking classes up to objects is just about all I use ORMs for -- write your migrations in SQL and write all queries that aren't braindead-easy (i.e. repository pattern stuff) in SQL/query builder.
- geodel 5y ago> If you haven't tried a type system better than what Java used to be, you owe it to yourself to try one of the other options out there. But those languages are not used for Enterprise Software development.
- Serow225 5y agoF# would disagree :) It's one of the nicest languages for Enterprise software dev
- hardwaresofton 5y agoWell if we followed this rule I think we'd all still be using punchcards? Or FORTRAN? It really depends on what your Enterprise looks like but I do agree that many corporations are going to be better served by more mainstream languages. That said, if you're worried about creating good maintainable software then types are the way to do it -- a lot of the changes that are landing in Java/Python/Ruby/etc came from the niche languages with the good type systems. The renaissance of typing (typescript, mypy, sorbet) is people essentially collectively coming to their senses and realizing that they want to know what their function is expected to be fed, at compile time.
- victor106 5y agoAs awesome as Go is, Java & SpringBoot are so entrenched in the enterprise world that Go has to come up with something much more awesome. 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.
- fnord77 5y agothe support tooling for java is fantastic. Profilers, APM tools that can trace webapps, visual debuggers and so on. Most of the tooling just works. We have part of our application written in another language/framework and while developing in it is fun and easy, it is a nightmare to debug and maintain in production. People complain about the verbosity of java, but that makes it great for projects where lots of people are working on it over the years. It is fairly easy to see what is going on. (spring complicates things a bit). Contrast this with say, clojure, where sometimes I have to spend an hour trying to figure out what a blob of code I wrote last year does.
- emodendroket 5y agoIf we're talking about Go, the tooling is good, your IDE will be pretty snappy because of the dedication to single-pass parsing, and you have to really work against the language to write complex and confusing code. The trade-off is that your first write can be more tedious (I really miss filter/map type operations for instance).
- roblaszczak 5y agoThe good news is that Go have very little entry point. Thanks to that you can hire people, who are working in other technologies and are interested in learning something new. From my experience they can be productive within a week. It's totally not possible with Java I guess ;-) It's probably a bigger problem to justify it on the company level. It's always some risk to go with a language that doesn't have such good position on the market ("Who will maintain it?", "Where you will find people who will fix it?", "Wouldn't this language disappear in 2 years?"). Fortunately, a lot changed in recent years. Thanks to Kubernetes, Prometheus, Docker and all other infrastructure Go is already used in the most of the companies. That's giving people much more confidence about Go.
- kevincox 5y agoFrom my experience generics are the most useful at the library level. For containers you obvious want it for many libraries it is useful. 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)
- bob1029 5y agoI've been around the block a few times on modeling complex domains, and have found that generics do not help in a fundamental way. 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.
- sidlls 5y agoGenerics isn't about the modeling of data, though.
- bob1029 5y agoI agree - its more about how you draw relations between types and work with them. If you are finding that a whole class of types can be processed or related in exactly the same way, then these are probably the same type. The motivation for using generics falls away when every domain type is fundamentally different in its shape and requires ad-hoc relations to be made on a contextual basis.
- valenterry 5y ago> If you are finding that a whole class of types can be processed or related in exactly the same way, then these are probably the same type. This is like saying "if a lot of different fruits can be processed the same way, then these are probably the same fruit" An example that makes it very clear is stringifying (e.g. for logging). You can stringify a user, you can stringify a database connection and you can stringify a boolean. But these three things are obviously not the same type at all. Therefore... > The motivation for using generics falls away when every domain type is fundamentally different in its shape and requires ad-hoc relations to be made on a contextual basis is wrong. However, bringing up the term "ad-hoc" is great! Because this is precisely what many languages are missing and where generics are absolutely needed. The languages that have the feature that you describe usually call it "type classes". Look up type classes in Haskell or Scala (if you are a JVM guy) or Rust where they call/implement it with traits (if you are a low-level guy).
- roblaszczak 5y ago> 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? 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 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.
- emodendroket 5y agoWell, the truth is a lot of applications don't actually really need generic data structures beyond those already provided (maps and dynamic arrays). If you do need that, yeah, you're in for nearly duplicate code. Over time I think you'll just start to approach problems differently and not really think about the limitation much, or that has been my experience.
- throwaway894345 5y agoI agree. For the most part, I rarely use anything but slices ("dynamic arrays"). Once in a while I'll use a map, but only for large Ns, since maps don't have useful zero-values and they tend to incur more allocations (which are relatively expensive in Go) than slices (and generally lookups are slower for small N values).
- emodendroket 5y agoI thought dynamic arrays might be a more recognizable term to people who aren't already Gophers.
- throwaway894345 5y agoFor sure; I wasn't trying to correct you--I just used both terms for clarity.
- peq 5y agoApplications don't need generics, but libraries do. The lack of generics is the reason for a lack of many useful libraries that I miss when writing applications in Go. Things I missed lately: Sets, maps with arbitrary keys, lists, sorting, removing duplicates, priority queues, parser combinators, list transformation functions (map, filter), transactions. Some of these exist for Go, but with awkward APIs.
- emodendroket 5y agoThe Go idiom for set is a map with bool values and given that it'll just return false for keys that aren't initialized I really don't see what you're missing compared to having a dedicated type. I'm not sure what your complaint is about maps, lists, or sorting. There are some specialized types you might occasionally want and I miss FP constructs too, but I don't think these are really serious impediments to productivity.
- throwaway894345 5y ago> Does everyone get around the lack of generics by making most generic-looking operations slightly-specialized structs and some interface composition? 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.
- hardwaresofton 5y ago> 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. Agreed, and I have to confess that it takes a lot of restraint for me to not suggest that people just change the languages they do generics and type programming in (in favor of haskell/rust). Proper algebraic data types, typeclasses + constraints instead of inheritance, good concise syntax -- there are better type systems out there but most people have only used Java/C# (which is fair since most enterprise software is written in those languages).
- gher-shyu3i 5y ago> Proper algebraic data types, typeclasses + constraints instead of inheritance, good concise syntax -- there are better type systems out there but most people have only used Java/C# What's ironic is that both Java and C# are getting ADTs (sum types) and pattern matching. Java already gives you exhaustive pattern matching with the upgraded switch statement. I think C# has a proposal for an HKT-like capability, though not sure how far along that is or how much of a priority it is.
- hardwaresofton 5y ago
- rmac 5y agomap[string]interface{}
- jerf 5y agoUse packages as layers: https://www.gobeyond.dev/packages-as-layers/ https://www.gobeyond.dev/packages-as-layers/ and you can probably write arbitrarily large Go systems, or at least, well beyond a point where most projects get. 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.
- sagichmal 5y agoLightweight DDD is a great match for Go. You don't want to go all-in -- you don't want a package usecases, for example. You just want to take the high-level concepts and apply them in a way that's idiomatic to the language. The Hexagonal Architecture is basically equivalent.
- closeparen 5y agoWe build enterprise-grade applications in Go 1.x. There's no trick. You just type a lot.