Why Clean Architecture Is Great For Complex Projects

Why Clean Architecture Is Great For Complex Projects

4 min read ·

Thank you to our sponsors who keep this newsletter free to the reader:

Software Design Simplified .NET Edition is a short and practical book for junior and mid-level engineers. You will learn encapsulation, cohesion, polymorphism, and more in one afternoon. Check it out here.

IcePanel realigns your engineering & product teammates on the technical decisions made across the business. Explain your complex software systems with an interactive map that teammates can drill down to the level of detail they need.

I've been using Clean Architecture for 6+ years on large-scale applications serving thousands of customers and millions of requests. Today I want to talk about why it's a great approach for structuring your applications.

I'm aware that Clean Architecture isn't a silver bullet, so I will discuss what types of systems can benefit from this architecture.

Clean architecture isn't revolutionary.

But it's prescriptive about how you should structure the code.

It's an evolution of layered architecture, focusing on the core domain and the direction of dependencies. All dependencies should point inwards, applying dependency inversion.

Here are some of the promises of Clean Architecture:

  • Maintainability
  • Testability
  • Loose coupling
  • Separation of concerns

It's independent of UI, databases, or external services - but you also need to be pragmatic (more on this later).

Let's dive in!

What Is Clean Architecture?

Clean Architecture was created by Robert C. Martin, aka Uncle Bob.

Clean Architecture is an approach to organizing a software system to separate the concerns of the various components. Making the system easier to understand and maintain.

You can think about Clean Architecture as a domain-centric approach to organizing dependencies.

There are similar architectures that follow the same domain-centric idea. You may know them as the Hexagonal and Onion architectures. They are more or less interchangeable with each other. And they all place the core domain at the center of the architecture.

This is my high-level interpretation of Clean Architecture:

There are four layers inside:

  • Domain
  • Application
  • Infrastructure
  • Presentation

Let's see what should live inside each layer.

Clean Architecture Layers

Here's a breakdown of what should live inside each layer of the Clean Architecture.

Domain

  • Contains the core business rules & logic
  • It should be independent of other layers in the system
  • Should be persistence ignorant - the persistence mechanism shouldn't influence your domain model
  • Examples: Entities, Value Objects, Domain services, Domain events, Enums, Repository interfaces

Application

  • Contains the application use cases
  • Contains application-specific business rules
  • Orchestrates the domain entities to perform business operations
  • It should be independent of external concerns (but it doesn't have to be)
  • Examples: Application services, Commands, Queries, External service interfaces, Exceptions

Infrastructure

  • Contains anything related to external concerns
  • Implements interfaces defined in the layers below
  • Examples: PostgreSQL, Keycloak, AWS S3, RabbitMQ, Kafka, SendGrid

Presentation

  • Represents the entry point to the system
  • Accepts data from the outside and passes it to the use cases
  • Acts as the composition root for dependency injection
  • Examples: ASP.NET Core, gRPC

Here's an example of how to structure the Clean Architecture on a solution level. You can also group related components together by feature. It leads to better cohesion and is an excellent option if your project is more complex.

I also made a few videos covering the Clean Architecture project setup:

Where Should You Use Clean Architecture?

Clean Architecture is very versatile and applies to various domains and systems.

But, you should play to its strengths and use it only when there's a tangible benefit.

I use the Clean Architecture when I want to:

  • Apply Domain-Driven Design
  • Solve complex business logic
  • Build highly testable projects
  • Enforce design policies via the architecture

If the above is true for your project, then Clean Architecture is an excellent option.

You should also consider the benefits of Clean Architecture.

The Case For Being Pragmatic

I try to be pragmatic when using Clean Architecture.

Applying what I like and having the freedom of "breaking" Clean Architecture if it will simplify things.

Can this be called Clean Architecture, then? No, not in the purest sense.

But, I still get most of the benefits of Clean Architecture.

Here's an example, when I'm applying CQRS in Clean Architecture to implement the use cases.

On the command (write) side, it's valuable to be independent of external concerns, so I will use repositories behind an interface. I can control the repository contract, and unit testing is straightforward.

But, on the query (read) side, I want to return the response as fast as possible. Creating an abstraction only adds indirection and reduces performance.

A better approach is to use the EF or Dapper in the handler and query the database. It's simple, fast, and you can use all the features offered by the ORM. You don't need a lot of complexity or abstractions on the query side.

I should call this approach Pragmatic Clean Architecture.

Closing Thoughts

Clean Architecture gives you a standard for organizing your solution.

You don't have to reinvent the wheel every time at the start of the project.

But, the layered structure and architectural constraints can increase the complexity of smaller projects.
So make sure your project is complex enough to apply Clean Architecture.

Another caveat of Clean Architecture is the danger of over-engineering.

Don't follow the principles religiously without considering the specific project requirements.
The overhead of maintaining so many layers and abstractions may not be justified.

Be pragmatic and try to make the best decision possible.

Sometimes that means straying from the paradigm.

Hope this was helpful.

I'll see you next week!


Whenever you're ready, there are 4 ways I can help you:

  1. (COMING SOON) REST APIs in ASP.NET Core: You will learn how to build production-ready REST APIs using the latest ASP.NET Core features and best practices. It includes a fully functional UI application that we'll integrate with the REST API. Join the waitlist!
  2. Pragmatic Clean Architecture: Join 3,600+ students in this comprehensive course that will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture.
  3. Modular Monolith Architecture: Join 1,600+ engineers in this in-depth course that will transform the way you build modern systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario.
  4. Patreon Community: Join a community of 1,000+ engineers and software architects. You will also unlock access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses.
  5. Promote yourself to 58,000+ subscribers by sponsoring this newsletter.

Become a Better .NET Software Engineer

Join 58,000+ engineers who are improving their skills every Saturday morning.