Categories
Architecture

Clean Architecture: The Bad Parts

Clean architecture is a common approach to designing software systems. However, there are some issues that might cause you more harm than good…

“Clean Architecture” is a software architectural pattern coined by Uncle Bob Martin in his book called, naturally, Clean Architecture 😅. It’s one way to structure software code that is an example of hexagonal architecture.

Ports And Adapters

The basic idea of a hexagonal architecture, otherwise known as a “ports and adapters” architecture, is that your domain logic and domain objects live in the “center” of your application. Other concerns like persistence, caching, etc. are treated as an add-on or “adapter” to the domain code.

ports and adapters

Generally, this is done by having your domain code expose any need for these infrastructural or application-level code as an interface.

This is conceptually similar to how we might design a computer, for example. A computer has a dedicated core purpose, but allows outside devices to “plug-in” via some kind of interface (a USB port, HDMI port, etc.)

ports

Principles Of Clean Architecture

Clean architecture has a number of principles that I’ll summarize here:

  1. Independent of Frameworks
  2. Testable
  3. Independent of UI
  4. Independent of Database
  5. Independent of any external agency

At first glance, I think most software developers would agree with these principles. I do, for the most part 😉 (we’ll get that that later…).

The most important principle that comes from both clean architecture and domain-driven design is to keep your domain objects and domain logic separate from other concerns.

We need to decouple the domain objects from other functions in the system, so we can avoid confusing the domain concepts with other concepts related only to software technology or losing sight of the domain altogether in the mass of the system.

Domain-Driven Design pg. 67 (Kindle)

How We Tend To Move From Conceptual Patterns To Implementation

The thing I don’t like about clean architecture is not necessarily on principle, but around how most developers move from thinking about these principles to implementing them into actual software projects.

The typical structure is to create projects or libraries that deal with:

  • Application layer
  • Domain layer
  • Infrastructure layer
  • UI layer

We tend to take these concepts and “one-by-one” just implement them as dedicated .NET projects, Java packages, etc.

I believe that this way of moving from the conceptual thinking to applying that as a specific “blueprint” of how you ought to structure your code is faulty.

Problem’s I’ve Faced With Clean Architecture

In one of the companies I’ve worked for, I brought in clean architecture as one of many ways to start improving development processes, development speed and product quality.

We implemented clean architecture for new features moving forward. While this was way better than the non-existent conventions and architecture that existed previously, there were aspects that caused issues.

One of the biggest ones was content switching.

Context Switching

For example, if you were working on a feature and needed to work on any combination of data access, domain or application logic, you had to totally switch contexts into a different software project. Into a totally different folder structure.

This seemed inefficient.

I decided that putting everything relating to a specific feature together, in the same folder, brought more benefits than not.

While respecting the dependency rule, this idea is the logical conclusion of following the principle of high cohesion: things that change together ought to live together.

But, doesn’t that lead to a muddling of concerns? Application, domain and database logic in the same place?

Loose Coupling

If you understand the principles of high cohesion and loose coupling, then this shouldn’t be a problem.

You can still acheive the “ports and adapter” like approach conceptually, but within each individual feature.

For example, the typical approach I take is something like this:

application design

If we “zoom-out” and are looking at this from the view of having multiple features or products, it looks like this:

vertical slices

Notice that I’ve grouped the “application” and “domain” layers under the same bucket. That’s because, in many cases, these can simply exist within the same folder or software artifact.

Within the same feature folder, if you keep your data access and other IO behind an interface, then “Bob’s your uncle.” 😋

Vertical Slices

At the time, I had come up with this approach on my own by using the principles of loose coupling and high cohesion.

As I stumbled through this way of thinking, I came across what is commonly known as “vertical slices” architecture.

Jimmy Bogard, as far as I can tell, is responsible for codifying this approach.

In my experience, this approach is simpler.

When adding or changing a feature in an application, I’m typically touching many different “layers” in an application. I’m changing the user interface, adding fields to models, modifying validation, and so on. Instead of coupling across a layer, we couple vertically along a slice. Minimize coupling between slices, and maximize coupling in a slice.

https://jimmybogard.com/vertical-slice-architecture/

It focuses not on the separation of concerns from a technical perspective (like domain vs. infrastructure vs. application logic) but from the perspective of reflecting the business domain’s features.

Again, keeping data access separate from domain logic is key, no doubt.

But, should that drive us to separate these things as separate software artifacts?

I think it makes more sense to separate business capabilities as separate software artifacts instead.

And now, we end-up in the land of SOA. But that’s another topic for another day 😅.

Do We Ever Swap Out Databases?

Proponents of the architectural style I outlined as the typical “clean architecture” might raise some objections to the vertical slices approach.

The primary one being: What if you need swap out your database from MySQL to Oracle?

My response would be two-fold:

  • How often does that actually happen? This sounds like premature optimization. I believe we can gain immensely more from keeping highly cohesive pieces together.
  • A vertical slices approach can actually make this easier!

About that last point: since we focus on making separate features in our system loosely coupled and isolated from each other, it follows that we can totally “re-do” an individual feature and keep that change isolated.

vertical slices

All too often, these migrations end up being a Frankenstein data access layer because it’s “half-way” into it’s migration.

Next Steps

If this interests you, here are a couple resources that can help you get started:

Consulting

Are you looking to get more help with applying these concepts to your own team and projects? I can help!

Let's talk!

5 replies on “Clean Architecture: The Bad Parts”

Thank you for another interesting article, James!

This topic is not well understood and the discussion should be kept warm! Implementing principles literally and dogmatically is always harmful…

Your idea reminds me Simon Brown’s “package by component”, do you see any contradictions in his and your approach?

Another thing which always confused me is the term “use case”. I can think about constructs like PlaceOrder or RegisterAccount. But, shouldn’t those be part of the domain? On the other hand, a controller is for me a concern from the application layer as its responsibility is not more than to validate requests, execute use cases and transform responses into the presentation mechanism.

Thanks!

Ya, I generally really like Simon Brown’s package by component approach. **If you create a component that aligns to a business function/capability, or in DDD terms a sub-domain, etc.

The part about “how do we deploy each component” is interesting also. I think that depends on the needs of that specific business function.

So, you might have a larger system – a modular monolith like Simon Brown advoctates (which I really like). This would be a collection of various components packaged as Java packages. So they are compiled and deployed together.

But, you could deploy those components using gradle (or whatever is more common with Java) as a downloadable package.

Then, one step up might be to deploy a particular component as an entirely different process.

So long answer is that yes, it’s very similar to his approach 😊

I like to use the term “use case” to refer as some behavior of the system that an external party (user, system, etc.) can perform on it. Thinking of how a user uses a system, for example, it’s fairly natural to think of an action a user can perform as a command or a query.

These essentially become the entry point into our application/domain logic (although they might be exposed by a facade or something like that, depending on your needs?)

So an MVC controller would “use” a use case to perform some domain command, for example. That same use case might be used somewhere else, like in a mobile API, to perform that same action.

Hopefully that clarifies things?

Thanks! Yes, I think our understanding of a use case is very similar, yours might be a bit more general. Behavior, an entry point to the domain logic. The same use case could be called from a REST controller, MVC controller or simply from a command line runner.

If it belongs to the application or domain layer is not so important (and maybe too academic) as the direction of dependencies is well understood.

I tried to implement this idea in one of my pet projects, you may check it out, the multiple (maven) artifact approach is implemented as well, in a separate branch: https://github.com/ttulka/ddd-example-ecommerce

Cheers!

This may be heresy, but I find it challenging to keep the domain model separate from the data model. Perhaps our customers (insurance companies and enterprises/municipalities that manage insurance) live in a data driven world where data drives the domain to react to changing realities. I’m getting dangerously close to believing it is best to model your domain as conceptual interfaces with operations but no attributes just to keep the domain model focused on concepts and relationships rather than data. For us, different customers require different “implementation details” so the domain is not all that stable, which I know is contrary to what Uncle Bob has to say about it.

Leave a Reply