When navigating through Line-of-Business (LoB) applications, you'll notice that most try to follow Clean Architecture and all its layers. I blame the tutorials and scaffolding tools that make it clear that you must separate your logic into Application Services and Domain Services.
I don't have much problem with the separation itself, but the unnecessary layers and numerous rules, just make the software way more verbose than it should be. I caught myself discussing and losing time, over and over, deciding if a class should go to the Application or Domain layers.
Let's break the paradigm
The first step to thinking differently is to ask why we do the things we do.
The reasons we have layers is to provide organization and fulfill abstractions.
A simple example of organization is separating all of your classes in the invisible layers of "types". I'm sure you named folders like Classes, Models, Requests and so on. Why are we abstracting by type?
Why do we need abstractions then? When we want two things:
- Hide the implementation
- Reusability
When you have too many layers you always have to think: Ok, where is the best place for me to put this?
I propose you this: instead of following the flow, create your classes very close to where they are being used. Don't hide them, don't early optimize. Make them pop and show that they are part of your feature.
Some people even named this "vertical slice" nowadays.
Evolution is the answer
You might think this idea is nuts... I agree, if we left things how they were. One critical aspect of software engineering is to keep evolving it.
We don't create a class and never modify it... If you created something so perfect at first, I guarantee you that you spent too much time on it (or you copied from someone).
I learned from looking at many different systems and most share the same issues: they don't evolve! I'm not talking about going from Monolith to Microservices... I'm talking about code organization evolution. As the system grows, you:
- Need to share core logic.
- You already know what won't change.
Don't believe me? Think about this: you start with a single class, maybe two or three and just a few flows. None of the classes are shared and the logic is quite simple/not reused.
Then a new feature needs to be developed, you create a 4th class, but now you need to validate against an existing rule from the previous classes.
Now you have a few alternatives:
- Copy and paste that rule
- Reference the class and apply the rule
- Refactor to a more "core" rule
I guarantee you, the first two options are what I see the most. The only true answer is to refactor your core rule. Possibly extract it from one of the classes and make it a standalone rule. Maybe you can also create a more specialized class that can enforce that rule, and that now is shared between all classes.
There's a really nice book Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy that talks exactly about that.
Don't let the "DDD" title fool you. This book goes through some real life and pragmatic decisions about evolving your software.
The gist of all this is: create something simple, evolve when needed.
The evolution can be as simple as a new class. But it can also be a new layer, new folder structure and even a new application!
Conclusion
If you don't believe me, please believe Vlad Khononov.
Don't follow architecture decisions blindly. Growing gradually and refactoring as you go is your best option.
I said that refactoring is the key, and I'm not joking. Take that seriously and as your application gets bigger, the refactoring effort also becomes bigger. But do not fall into the trap of: I will just refactor this bit here and leave the other part of the system how it was. I've been there and the results are catastrophic!
Don't be afraid of failure. I guarantee you, if you don't spend time over complicating things back then, you will have plenty of time to do the right thing now.
Have you ever felt "trapped" by a Clean Architecture template? Or do you think the strict separation is worth the initial boilerplate? Let's discuss in the comments!
Top comments (0)