58 comments

[ 2.1 ms ] story [ 128 ms ] thread
My mind fused title "Your DI framework" into "DIY framework"
For some reason this winds up being worse in the .net world than the java world. The main trouble I see with spring is not that it messes up your code but that people use stackoverflow to 'get things done' which leads to no overall strategy and thus mystery about how things get initialized.
This is just the anemic vs rich domain model debate, the standard retort being https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain....

TL;DR: rich domain model causes an explosion of coupling (your User class is now coupled to your database, your screen, your rendering engine, your printer, etc, when all it really is is some user data). And the rich domain model simply breaks down when you need functionality on two objects (should it be user.render(engine) or engine.render(user); either way one of your domain objects needs to know some "private internals" of another). With the anemic domain model these problems don't exist.

The coupling you mention was solved long before DI became popular. The service locator pattern isn't as transparent as DI but it solves the problem of course grained service dependencies.
You mean "coarse-grained", right?
This is a gross misunderstanding of a rich domain model. A domain model should depend on nothing externally, except via interfaces which are a part of the domain model.

The real tl;dr is that if you can't run and test your domain model without a database, UI, external services etc, it likely is not a domain model in the first place.

Your misunderstanding is common when ignoring the provenance of the pattern: the place for it is "when you have complex and ever-changing business rules" (page 116 of Patterns of Enterprise Architecture). If "user.render(engine)" or "engine.render(user)" are operations in your domain model (and are, as I assume from your post, UI related operations) the domain model is being used as an architectural pattern rather than a business logic organization pattern, in which case none of the stated benefits hold because of misuse.

I can't help but disagree with so many of the things he says.

I won't even go into things like "Does your customer know what an OrderPriceStrategyFactory is for? No, then it’s not a real thing. Its some bullshit you made up.", but statements like "If we change how we contact customers then only the customer needs to change, not also the ReportBuilder" sound overly simplistic to me. The whole point would be that nothing needs to be changed. You just write a new ReportBuilder that implements the same contract/interface and switch the wiring. No changes in the existing ReportBuilder or Customer classes.

That, or maybe I'm just completely misunderstanding what I'm reading. Which isn't entirely impossible :-)

I don't think the author makes a good case for the problem with, for example, noun-verbers, other than that they aren't very OO. If OO means putting lots and lots of state into objects, then I'm happy to be the lunatic fringe.

I like my objects as a unit of code organization and my value objects as simply that - an immutable representation of a value.

Every business will have an order price factory in it. Prices aren't just picked by the roll of a dice!

If you're trying to model the inner workings of a business, you probably don't want to be starting with the customer's view of it...

Yet another time it comes up. But now, after functional programming being commonplace, we at least know the answer to the "why" question.

Consider the "classical OOP" method signature:

email.send()

vs "the anemic way"

emailSender.send(message: Email)

If there is only one way to send email (SMTP), things works fine. But let's assume we have to implement another way (e.g. Mailgun API). In the "anemic" case everything is simple: extract interface from EmailSender, add new implementation, all other method signatures remain unchanged. Refactorings are simple and can be done easily in any modern IDE. However, in "classical OOP" way, we have to do something like:

email.send(sender: EmailSender)

Which changes Email::send signature, which leads to breaking API changes and extensive manual refactoring of the entire codebase.

The "anemic way" had won because it is much more reusable and maintains API stability. Incidentally, this approach have smoothed the transition to functional languages — "NounVerber" objects became modules (collections of functions), and value objects became immutable data types.

> Which changes Email::send signature, which leads to breaking API changes and extensive manual refactoring of the entire codebase.

Only if you decide to change the signature, there are other ways to achieve the same result.

For example?
"GetMailPolicySingleton()->SetSendingMechanism(Email::SMTP)" ;)
Sometimes you're sending via SMTP, sometimes via MailGun. If you don't remember to call GetMailPolicySingleton()->SetSendingMechanism() before every email.send() you end up using whatever policy was set last.
> Singleton

What if two threads need to send mail via SMTP and Mailgun...

Singletons have nothing to do with reentrancy or lack thereof.
A 'sending mechanism' property on the email object that defaults to SMTP?
This. There may be other reasons to dislike the email.send() formulation, but it seems like the need to support other mail APIs could be handled relatively easily within this type of OO approach through the use of default values.
I suppose you could add more methods, e.g sendEmailViaMailGun. But that results in the invoker having to have if-else logic (itself a code smell) for selecting which method to call, which is bad for SRP. You could try to fix that by encapsulating that logic into an EmailRouterStrategy class, but that's just another dependency you have to inject, which is not ideal according to TFA.
Couldn't you just add a new SendMethod optional parameter defaulted to the method that was used prior to the refactoring? No breaking API changes, no extensive manual refactoring of the entire codebase, as far as I can tell. This is how I always did refactoring for years, it's immediately obvious to support developers wtf is going on, and there's no requirement to have any "complicated" architecture on every single object in your entire application. Instead, the code largely resembles the simplicity of the real world being modeled.

Is this approach is so dangerous (as we're constantly told) why has it never caused me any problems? Maybe I'm missing something but I've never read an article that explains what it is I'm missing in a way that clicks.

> Couldn't you just add a new SendMethod optional parameter

Adding an optional parameter to an already existing method is not always a backwards-compatible change. For example, in C# this will effectively change method signature and break API.

Exactly! This is why Lisp has dynamic variables:

    (let ((*emailer* mailgun-client))
      (send email))
Or, using Go contexts (which are just a way of hacking in dynamic variables):

    ctx = email.NewContext(ctx, mailgunClient)
    email.Send(ctx)
And in email.Send:

    emailer, ok := EmailerFromContext(ctx)
    if !ok {
      emailer = defaultSMTPEmailer
    }
TFA actually suggests having a "RenderAsEmail" method on the "Report" object! That right there should have been a hint that this line of reasoning is flawed. It would result in things like mixing presentation logic along with business logic in the same class. The author realizes this but does not seem to see it as a red flag.
Spot on. You replace your "anemic way" with first-class functions and namespacing, it ends up being the same thing.

With currying, you do away with OO and parameter initialization altogether.

    @curry
    def SMTPSender(host, message):
       ...

    emailSender = SMTPSender(host='localhost')
    # partially evaluated fn with `host` fixed
    emailSender(message=message)
This is pretty much how we deal with this in a DI framework anyway. The constructor of the transport takes the host and the send method takes the message.

   emailSender = SMTPSender(host='localhost') # in container
   emailSender.send(message)                  # in implementation
The consumer of the SMTPSender only interacts with the send() method.
The anemic way is modeled correctly anyway.

An email doesn't send itself nor does it interact with the transport. The transport (MUA/MTA for example) is responsible for delivering it via whatever method it happens to support. The container delivers the transport to the code. The consuming code doesn't care what the transport and method does, merely introduce the two and let them make sweet email love.

The article stinks of circa 2002 purist babble.

Noun-verbers are also really helpful for testability, not just refactoring. Using a decent mocking framework (or just rolling some custom mocks) I can test almost all my business logic through unit tests, reducing the time that tests need to execute and increasing their reliability
Excellent summary. What an oop language like Java really needs these days are immutable value classes.
did you read the blog post even ? He was calling for mutability in the classes and for the state to be closer to the data object itself.
I remember being torn reading articles about anemic [considered harmful] around 2004 because being paranoid, immutable, functional, data first fits my mind a lot more.

It's hard to swim the trends.

> The "anemic way" had won because it is much more reusable and maintains API stability. Incidentally, this approach have smoothed the transition to functional languages — "NounVerber" objects became modules (collections of functions), and value objects became immutable data types.

I agree. By the way, you can do that even in imperative languages. I do it all the time in Python or Go.

"Almost certainly: if you’ve got value objects and noun-verbers, your design is rubbish."

If the code is readable, understandable, testable, and it does the job - who are you to say it's rubbish?

I think the problem is that OO is a term that people don't understand anymore. The author is right about OO principles and the fact that most people don't follow the traditional usage. The thing is that good OO isn't actually good code.

The world has moved away from strict OO because it's not very maintainable.

I've been seeing quite a lot of this kind of "OO reactionary backlash" recently, "... is an anti-pattern because OOP". Most of it already years old, but still something that feels like a little trend suddenly showing up in links left and right. Could be a sign of the last "pure OO" advocates starting to feel the pressure, or it's just me googling the wrong topics...
"One of the properties of good OO design is that code that operates on data is located close to the data."

Says who?

I say the point of good OO design is to have very clear focused responsibilities for classes, very clear layers of abstraction, and concretely declared dependencies.

This way I know my business logic doesn't sprawl, is discoverable, can be replaced modularly, and tested extensively and throughly.

This author sounds like he has no idea what it takes to actually build a maintainable code base of domain-driven business logic.

The fact that he actually thinks "Order.SubmitForPicking(), UserAccount.UpdatePostalAddress(), and Basket.CalculatePriceIncludingTaxes()" are examples of good OO design is LAUGHABLE. This approach couples your data layer with your business logic, negates the ability to use mocks, and makes refactoring WAY harder than it needs to be.

Well, encapsulation was one of the three legs of the OO stool, especially at a time when languages like C/C++ offered no protection against reading/writing memory anywhere in a program's data or code segments. Encapsulation back then literally meant keeping the representation of the data close to the code, i.e. in the same class that implemented the interface on the data or a closely-related specialty class. So I would say the author is not incorrect in looking askance at "value objects" that are passed around, from that perspective.

On the other hand, I'm not sure the perspective is quite as important as it used to be. Modern dynamic languages make it harder to do bad things in memory, and passing value objects around is pretty much the arterial life force of the entire Internet. In any case one thing is sure: the more pieces of code that know the internal structure of a bit of data the more dependencies there are, and the harder the program is to work on. So to the extent you can have that knowledge in one place - the implementation of an interface - you're better off.

Perhaps more importantly, OO purism just seems a little anachronistic to me today. I think the key things that OO had to teach us have been mostly absorbed, and we've moved on. It's still there, but now it's part of the fabric of the art and not as obtrusive as it was in the 90's.

OO concepts live on whenever we are talking about interfaces, contracts and the like. The failed experiment is "true" object oriented domain modeling, the idea that software representations of real entities (an order, a dog, a car) should include code beyond maybe some basic constraint checking. Other than that, thehe OO tools are still powerful and some form of encapsulation is essential for any data structure that is more complicated than a pain array or a one-way linked list. But these structures are not representations of real objects, they are actual data objects. A hashmap isn't a representation of a hashmap, it is the real thing (even if it maps representations of dogs to representations of cars).
Sorry to nitpick: the point of OO is exactly what the original rant was advertising: compile your domain representation into furry little blackboxes that live inside the computer and interact with each other. Your point (and the HN hivemind consensus, because even the FP purists will agree) is that OO tools are better used in ways that do not follow the OO design principle.
My comments from the page, awaiting authorization:

"See, what you are suggesting is actually a strong violation of the single responsibility principle as well as separation of concerns. This is exactly like the code I used to write. While it really is easier to manage when it's small, it's a naive approach that doesn't scale well.

In a real life example of what you are suggesting, our User and Building classes for instance ended up over 10,000 lines long, doing things even just tangentially related to their parents. The real kickers were the cross business object ones who required more than one. Is it on Class X or Class Y? Sometimes it would end up on both accidentally causing the angels to cry.

About two years ago we started a massive rewrite, and now we have lots of small consice objects that perform very specific tasks. They're logical, organized and dependency injected. I would never wish the reverse on anyone."

Yes! I was lucky enough to start a green-field project recently and basically did exactly what the author is advocating against. So far it's been the most extendable, easy to read and easy to maintain project I've worked on.

The author briefly mentions the Single Responsibility Principle and then tramples all over it, suggesting you put your 'code close to your data', shoving your verbs onto your models? Good luck ever refactoring. You'll have orphaned properties and methods that people are afraid to remove because they may anger the gods. You'll have flags, flags everywhere to control how your model does its work. Give me small, swappable, testable noun-verb classes any day of the week.

You know, most code just shouldn't scale on this manner. But yes, there's some code that must.

I guess the main flaw of both the article arguments and yours is assuming anybody knows a set of universal rules that apply to all software development.

I'm not a big fan of DI frameworks but none of the arguments in this article ring true for me.

His proposals for how OO should work are half the reason I started shifting toward functional languages years ago. OO learning from Functional is a good not bad thing.

How come OOP was supposed to be the best way to code and now we need dependency injection to make it practical? Does that mean that before DI, OOP was never being done properly? It was a bad idea all along and everybody thought it was just their poor OO skill that made them unable to design programs that met all the requirements?
An article by someone who apparently doesn't understand composition...
imho a very shallow understanding of oo. the arguments are only true for very simple models. when you have operations that manipulate multiple objects or have some other cross cutting concerns these models completely break down.

on the other hand: IoC containers try so solve a let-down of oo...we have to come up with something better- functional programming?

The author suggests a naive approach to object oriented programming. When all functionality is a method of the data it represents, these objects become hideously complicated and unmaintainable.

An example is comparing Objective C's string class, to .Net's. In objective C, path manipulation methods are on the string object itself; but in .Net, path manipulation methods are static methods in the Path class.

The .Net approach is better. Why? Do all strings represent paths? No. There's an unlimited number of ways and reasons for manipulating strings; and making them all methods of the string class is unsustainable.

This talk might be of interest Stuart Sierra - Components Just Enough Structure https://www.youtube.com/watch?v=13cmHf_kt-Q - it is a tiny clojure framework but the ideas should be thought provoking, although granted with OO aligned type system not completely adaptable. In this example Customer could possibly just be a data-bucket/map/hashtable/Dictionary, Im not entirely sure how I'd handle reporting though as the example has put me back in OO mode, but a customer report (which knows how to organize data about a customer) shouldnt be part of customer entity (used loosely) in my opinion.
If I understand the argument correctly, this is a problem that some DI frameworks already address. And in others, there's an easy workaround.

Say you have a class CustomerHandler (stupid example, pseudo-C#) with a constructor like this:

     public CustomerHandler(Guid customerId, ILogger logger, ISomeExtraDependency whatever) { ... }
Now, let's say another class needs to create CustomerHandlers for specific customers, but doesn't want to care how they are created. Autofac, the .NET DI framework I'm most familiar with, let's you inject factory delegates:

    public RequestProcessor(Func<Guid, CustomerHandler) customerHandlerFactory) { ... }
(Every type not mentioned in the Func<> is auto-injected.) This is very convenient because you can change the dependencies of CustomerHandler without messing up the rest of the code.

Of course, even in the absence of such an auto-factory feature, you can still create a dedicated CustomerHandlerFactory class and inject that. That's not quite as convenient, but it's still a lot better to have only one extra place that has to know CustomerHandler's dependencies than having them all over your codebase.

A couple of miscellaneous thoughts:

1) The problem that I've noticed with the traditional OO designs that I've worked with is that bits of similar functionality tend to get scattered about the code base. For example, the author creates a CreateReport() method in the Customer class. But if later, we have, say, an Account class (not equivalent to a Customer) that also needs a report, we find ourselves with two likely similar looking methods. I've found this to become problematic as the codebase grows, and ultimately your code exhibits a massive DRY violation.

2) His complaint about singletons is interesting but I think irrelevant. In Spring, for example, everything tends to be a singleton (the default), and stateless, so indeed, your code could be replaced by static methods. Not that this would be a good idea, although in some cases it might make sense. (The Play framework does this: http://stackoverflow.com/questions/5192904/play-framework-us...)

3) The author needs to read Yegge's http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom....

> "A Customer can CreateReport(), a Report can RenderAsEmail(), and an Email can Send()"

Until you need to generate a different type of report for the customer, render a different format email, or send the email via another process. At that point, you have 3 options:

1. inherit and override. So you end up with

* SpecialReportCustomer : Customer,

* NewEmailStyleReport : Report,

* SendViaProxyEmail : Email

It should be pretty obvious that this won't scale... at all.

2. Inject the desired sender or report generator into your object, depending on what implementation of the report builder or email sender you want. But why would the object have to know how to print itself, or send itself? Separation of concerns, people!

3. Break the interface of your class, make the methods accept a special reportBuilder or EmailSender... but why do that, when you can just do literally the opposite of what the author says, and have option 4:

4. Use an CustomerReportBuilder.Create(customer), or EmailSender.Send(email).

...and then you see why option #4 is your best choice, and why this article is objectively wrong in every way.

Agree, I also find useful to split between domain concerns and application concerns.

A report is something that belongs to business, but the fact that it could be sent via email is totally accidental and depending on the technology we use (as it is the persistence layer, or the protocol which exposes the app).

Domain concerns live on domain object (which knows only other domain objects), while application concerns live in services. And here is where you have interfaces, concrete implementations and, where necessary, factories to switch or add the implementation at the flip of a switch. Eg: a ReportMailSender which implements a ReportSender, that someday get replaced with a SlackSender implementing the same interface. You want both? Wrap them on a AggregatedSender.

Now your DI framework is getting really useful.

I wonder if the author has ever worked on any relatively large projects. This has to be the worst way you can write your code. I know I wouldn't like working on anything written in that way. That's how you end up with 10,000 line files.
The anaemic way would turn that 10,000 line file into something closer to 10,000 one-line files. To me, that is the very definition of spaghetti code.

My biggest problem with the enterprise Java way of doing things is the fact that you have to open 18 files just to figure out how the hell one small bit of functionality works.

The other design smell with these noun-verbers is they’re almost always singletons. Oh you might not realise they’re singletons, because you’ve cleverly hidden that behind your dependency injection framework: but it’s still a singleton. If there’s no state on it, it might as well be a singleton. It’s a static method in all but name. Oh sure its more testable than if you’d actually used the word “static”. But it’s still a static method. If you’d not lied to yourself with your DI framework and written this as a static method, you’d recoil in horror. But because you’ve tarted it up and called it a “dependency”, you think it’s ok. Well it isn’t. It’s still crap code. What you’ve got is procedures, arbitrarily grouped into classes you laughably call “dependencies”. It sure as hell isn’t OO.

What's so terrible about static methods? If you need to perform some sort of calculation that doesn't mutate any state, isn't a static method the correct choice, at least when you're working in a language with no functions?

A procedure with no side effects is really just a function, is it not?

I don’t think the author completely understood the DI frameworks. Just because a service in a DI framework is often a stateless singleton doesn’t mean they don’t allow stateful injection: EntityManager for JPA is a great example. Sure, there are other services with absolutely no state, but don’t blame the frameworks for promoting statelessness, it’s a practice promoted by many other software designs e.g. REST. I believe I have enough experience to say that state replications in a clustered environment does not scale and is not worth the pain.

Order.SubmitForPicking() sounds great on paper, that’s until you realize in order to submit an order for picking, you’ll need other value objects such as Customer, Account, Warehouse, Picker, Robot, Supervisor, Audit information …. the list goes on and on. How are you going to inject these info into an Order? And do they really belong in an order? I certainly don’t want to see all of these info in my JSON when I retrieve a single order on the client side.

The moment you move behaviors close to a value object is also the moment a plain object is no longer a plain object. Furthermore, a behavior laden value object becomes hard to evolve, extend and modify. There’s no clear boundary of where data ends and behavior begins, you certainly cannot package them up as a library and give it to your clients, you’ll have to create a separate set of value objects for that and start a maintenance nightmare.

It's kinda sad that Java Code Geek rejected my comments (and many others') and we have to come to Hacker News to express our views.

For me anemic model is much more natural and easy to use. Object-Oriented Programming is not a good approach to a general architecture. It has its place in some layers, e.g. GUI API usually can be successfully modeled with OOP, but otherwise just use modular procedural approach and use object-oriented constructs very conservatively, unless it fits naturally. That's the best approach for me and it works flawlessly. Of course Functional Programming, Logic Programming has its place too, thanks to modern languages like Scala, where you don't have to choose the only one paradigm.
This seems to be a complaint that some code doesn't correspond to the author's notions of "good" OO code, without a clear picture of what "good" OO code is except for a few vague references to fidelity to the domain without much discussion about what good domain modeling is.

For me, one of the clearest ways of analyzing a system in domain terms and working with it with business users to is with technology neutral specs in something like a Yourdon-style DFD, with a supporting data model and process descriptions.

It then becomes clear what the relevant nouns are in the domain: they fall in two basic groups:

(1) entities that correspond to named data items shown in flows or stores on the DFD (which will also, of course, be represented in the data model), and

(2) entities that correspond to named boxes and bubbles on the DFD, particularly processes, stores, and external interfaces.

A system whose implementation maps closely to this kind of requirements specification is a lot like an anemic data model system, with most of the classes being either immutable data objects or process objects (the latter of which may have "NounVerber" names if that convention is used), but that's because the processor is a real noun in the domain -- in a non-automated implementation of the system, its a role that a human actor would take on in executing the business process.

You do end up with state stored in (or stored externally and managed by) objects with methods which mutate state, but these tend to be the processor objects and the stores, not the data objects that are transferred along flows.

It seems to me that the "rich domain model" approach is mostly a failure of domain modeling that apply ideas from OO's origin in simulation modeling without recognizing that, in most other domains, the analogy to simulation modeling only works if you recognize that applications tend to be "simulations" of an idealized process executed by idealized technology-neutral actors acting on abstract data constructs, and that those idealized actors -- the processors -- are key elements of the domain model. Rich domain modeling ignores those and attributes their behavior somewhat arbitrarily to data objects, which end up giving data objects multiple, tangentially-related responsibilities (and provides lots of rooms for debating where certain responsibilities belong.)