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…

Note: I created a talk based on this article that you can view here.

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


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.

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:

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


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:


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.

Thanks for the article, James!

In practice (at least from my experience), modularization/packaging by features can lead to a lot of problems. Usually developers tend to share code across features, so if you want to avoid it, you have to duplicate some logic or introduce domain events to coordinate changes across the features. As an example you can see that you have “core” feature, which is quite isolated and basically include full onion with domain, adapters and frameworks. Then another feature needs to query some part of business logic from “core” feature, in practice developers usually simply share the repo or service/use case and that’s it. All dependencies at the end…ball of mad πŸ™‚

I always start with layering and increase cohesion within the layers, when I see that some part can be sliced into separate modules/features I do that. If you start in new domain and in new application, starting with the features is a bit awkward, you simply do not know the domain and you assumptions about the bounded context is not full.

Regarding UseCases, I actually do not like this naming. If you take classic DDD book – application services is much better naming from my point of view, but in that case clean architecture lacking pretty import concept as domain service…but that’s another story.

Ya, different approaches to development or design have different names for the same things lol. “Command” vs. “Service” vs. “Use Case” vs. “Action” vs. ‘Behavior” vs. “Function”… πŸ˜…

Hi James, maybe I’m totally missing the point but I don’t understand how you can defer technology decision with this kind of architecture. Let’s imagine I’m starting a new software and I don’t know yet which persistence technology I’ll use. To start, I decide to go for an in-memory collection, but only later I decide to go for Entity Framework. How do you handle such a scenario in your tests? If I check Jimmy Bogard’s vertical slices architecture repo, he only has “integration tests”. Does this mean that, with a vertical slices architecture, you’ll only have “integration tests”? If for some reaon, finally, entity framework is not adequate for a use case, he’ll have to change all his tests for that particular use case…

You can put an interface behind any persistence logic and (a) stub it out with a fake (like an in-memory store) and (b) just mock it out. As long as you make sure that the code that is persisting things is behind an interface and is not doing any business logic…then you don’t need a database, etc. at that moment.

It’s possible and might be the right choice for your team, project, budget, scope, etc. It’s not a single faceted decision.

You might have opted to do something like Jimmy Bogard’s example. The benefit of a vertical slice architecture is that you can use different persistence technologies PER slice very easily. Instead of having one re-usable “repository” or whatever, each vertical slice would just have its own data persistence code. That way you aren’t coupling each slice to the other. If one needs to change, as you suggested, then you can change a slice (and its tests if you are doing integration tests) and know that it won’t break or affect another slice.

Hopefully, that helps πŸ™‚

Clean architecture is based on its interfaces and dependencies rules, this is not an alternative to clean architecture because CA does not limit assemblies to layers, even more I remember Uncle Bob keeping this point open with the possibility of keeping all related layers together.

Yes, I agree. This is why in the article I said:

“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… 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.”

Leave a Reply