← Back to context

Comment by radicalbyte

6 years ago

The Java example you've provided is known as Domain Modeling, it's well described (and honestly greatly expanded on) in Domain Driven Design by Eric Evans.

It's applied heavily in the enterprise world simply because it's so powerful. In general as the domain logic becomes more complex the benefits of doing this increase.

Actually, not encountering this style in code-base which is solving a complex problem is a massive warning sign for me. It usually means that concepts are poorly defined and that the logic is scattered randomly all over the code-base.

Apps Hungarian is just a logical style: name functions, variables, types so that they're meaningful within your domain. The result of which is code which is very easy to understand for someone who understands the domain. This doesn't mean long names - if you're doing anything math intensive then using the short names which conform to the norms of the field is perfect. For a business process it probably isn't :)

>Actually, not encountering this style in code-base which is solving a complex problem is a massive warning sign for me.

There is a fine line between capturing some common meaning in a reusable type (e.g. Optional<T>) and spending most of your time crafting a straight-jacket of type restrictions that are supposed to thwart some imaginary low-level bug in the future.

Java is actually a great example of how this mentality backfires in real life. The language, from day 1, had a zillion ways to force someone else to do something they didn't want to. Abstract classes. Private methods. Final classes. Etc. It was supposed to ensure good design, but in practice just led to the endless reenactment of the banana-gorilla-jungle problem. (https://pastebin.com/uvr99kBE)

At the same time, the language designers didn't add such basic features as lambdas and streams until 1.8. This lead to creation of ungodly amount of easily preventable bugs, since everyone was writing those awful imperative loops.

  • Banana-gorilla-jungle problem results from OOP. This article (and presumably, the GP) is describing encoding domain-driven logic in a statically-typed functional language, and thus the OOP issues are not a problem.

    Functional languages don't carry around their environment with them, so you just get a banana. No gorilla or jungle.

    • Oh, it does have some banana-gorilla-jungle problem, it's just not at the same place.

      If the types are computationally equivalent, there is a plentora of type classes you must inherit, there is a lot of packing and unpacking to make them work with standard libraries (how do you print an address?), and if the values are not completely separable, there are plenty of conversion issues.

      It is an improvement, no doubt about it, but the main benefit of the languages with advanced type systems is that you can refactor code easily to add them only to the places where it's a win, only after your code is complex enough to make it a win.

    • >Banana-gorilla-jungle problem results from OOP.

      Not really. There are specific design decisions that Java made which enabled this issue to manifest. I've recently took a foray into Smalltalk and it doesn't have this problem, because methods depend on object interfaces rather than class types. AFAIK, OCaml also got this issue right while keeping static type checks - they decoupled classes from types.

      >Functional languages don't carry around their environment with them

      It is entirely possible to recreate the issue in a functional language, as long as there are sufficiently powerful mechanisms to enforce type safety during compilation. In fact, if you don't understand this and follow the advice in the article too far, that's exactly where you will end up. You will have a jungle monad containing gorilla monad holding hostage the banana data structure the user actually cares about.

      1 reply →

  • Java has just had a terrible steward in Oracle. Plus the community seems to like boilerplate code (see: Angular for more of that if you're into javascript).

    I switched to C# as my main language specifically because they were gaining awesome features like generics, lambda, LINQ. Since then it just got better and better.

    In the C# world you seem to encounter systems where the logic is either in:

    * UI event handlers * Methods on helper classes which consume a model generated from a database. * Domain modeled with classes with the logic split between the classes themselves and services which consume classes.

    In the first two cases the code tends to have a massive sprawling dependency tree which makes it harder to maintain. In the latter case you've usually got a better chance of having properly isolated code. Even if the isolation isn't super you at least have the logic well organised.

  • it's extremely useful especially at interface level but also in day to day coding, pity the people whose method signature is (string, string, string) because no ide can save them from the churn.

It's particularly powerful if you can great an algebra of your domain. You can define invariants which are then enforced by the type system.

I use this a lot when writing C# code and the whole experience is getting better with every new release. Microsoft keep adding awesome stuff like ADTs which make working with these types a dream.

  • Do you have any resources you can recommend which describe how to create an algebra of a domain?

    • I’d recommend Scott Wlaschin’s excellent book “Domain Modelling made Functional”. The examples are in F# but the concepts apply much more broadly.

      7 replies →

> Actually, not encountering this style in code-base which is solving a complex problem is a massive warning sign for me.

This is very narrow-minded. DDD as a broad concept is good and something I practice.

DDD in enterprise is a disastrous nightmare. When people on your team are wasting time Googling lingo to try to figure out what kind of object they need, where to put a file, or where a method should go, it's a huge red flag.

I was on a team that seemingly spent all its time trying to figure out what the hell DDD was prescribing, let alone trying to figure out how to do it.

  • > When people on your team are wasting time Googling lingo to try to figure out what kind of object they need, where to put a file, or where a method should go, it's a huge red flag.

    I agree, but for a different reason. If you've got a team of programmers on a project without any understanding of what the client is doing you're bound to implement stuff ass-backwards from the clients perspective.

    DDD is fine in enterprise, but the domain does need to be communicated to all involved parties. You can't just dump a codebase on someone and expect them to get to work, that only barely works for non-DDD codebases let alone DDD codebases.

  • The kind of DDD this article is describing is totally different from Java-world enterprise DDD. It's DDD done right, with no 50-member OOP classes. Just a single function that can only take a type that describes your domain in a way that prevents whole classes of errors.

    • I think we're on the same page.

      When inheriting a purist DDD project written in C#, I came across several... interesting... charts, including this one:

      https://jj09.net/wp-content/uploads/2018/12/ddd-diagram-exam...

      A lot of members of my team couldn't figure out where things were "supposed" to go (or what most of the terms mean) after weeks of working on the code, so they just started slapping methods anywhere and worrying about it later.

  • > [...] trying to figure out what the hell DDD was prescribing, let alone trying to figure out how to prescribe it.

    Well... If the carpenter needs to Google how to use a hammer and nails while at the construction side, something went terribly wrong, didn't it?

    • I would actually agree with you there, but I suspect that wasn't your point.

      A hammer is such a simple and obvious concept that it should not need to be googled. If anybody needs to do that anyway, you should not put the blame on the carpenter, but on the guy who "designed" the hammer.

      On a related note, I still very much am convinced that Don Norman's Design of Everyday Things is a great read for anyone who designs APIs; types in this argument function both as affordances and force functions, which are the exact reason why a hammer is such an obvious tool.

    • You seem to be implying that a dev who doesn't understand enterprise DDD is unqualified.

      That's not like a carpenter who can't use a hammer. It's like a carpenter who asks for a sketch of a project and instead receives a phone-book sized list of vague instructions that no one can agree on.

      6 replies →

This can go two ways. One can end up with lots of different types that do nothing but wrap a string. On the other hand I have also seen a float being used to represent a price. The correct way is somewhere in between those two extremes. If you have some logic specific to a value, by all means, make it a type but if there isn't it just ends up with lots of boilerplate.