
Your DI framework is killing your code - mrrazz
http://blog.activelylazy.co.uk/2015/09/25/your-di-framework-is-killing-your-code/
======
atemerev
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.

~~~
orf
> 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.

~~~
atemerev
For example?

~~~
to3m
"GetMailPolicySingleton()->SetSendingMechanism(Email::SMTP)" ;)

~~~
taspeotis
> Singleton

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

~~~
anton_gogolev
Singletons have nothing to do with reentrancy or lack thereof.

------
deanCommie
"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.

~~~
markbnj
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.

~~~
usrusr
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).

------
donatj
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."

~~~
rescripting
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.

------
daxfohl
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...](https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-
is-no-anti-pattern-its-a-solid-design/).

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.

~~~
anthony_d
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.

~~~
facorreia
You mean "coarse-grained", right?

------
efdee
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 :-)

~~~
to3m
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...

------
valdiorn
> "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.

~~~
dandlift
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.

------
gwbas1c
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.

------
Asbostos
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?

------
jasonlfunk
"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?

~~~
anthony_d
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.

~~~
usrusr
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...

------
msluyter
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...](http://stackoverflow.com/questions/5192904/play-framework-uses-a-lot-
of-statics))

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

------
kyllo
_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?

------
zaphar
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.

------
tassidevil
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.

------
codeflo
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.

------
estefan
An article by someone who apparently doesn't understand composition...

------
PaulHoule
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.

------
nowprovision
This talk might be of interest Stuart Sierra - Components Just Enough
Structure
[https://www.youtube.com/watch?v=13cmHf_kt-Q](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.

------
choward
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.

~~~
gazrogers
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.

------
vbezhenar
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.

------
dragonwriter
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.)

------
verinus
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?

------
dvh
My mind fused title "Your DI framework" into "DIY framework"

