Now, let’s revisit our layered application using Clean Architecture, starting with the core layer. The core project contains the domain model, the use cases (services), and the interfaces needed to fulfill those use cases. We must not access external resources in this layer: no database calls, disk access, or HTTP requests. This layer contains the interfaces that expose such interaction, but the implementations live in the infrastructure layer.The presentation layer was renamed Web and lives in the outer layer with the EF Core implementation. The Web project depends only on the Core project. Once again, since the composition root is in this project, it must load the EF Core implementation project to configure the IoC container.Here is a diagram representing the relation between the shared model and the new Clean Architecture project structure:
Figure 14.17: From shared project to the Clean Architecture project structure
In the preceding diagram, we took the center of the classic layered solution and merged the layers into a single Core project.
Here’s the link to this project on GitHub: https://adpg.link/rT1P.
Most of the code is not that relevant since, once again, the most significant aspect is the dependency flow and relationships between projects. Nonetheless, here is a list of changes that I made aside from moving the pieces to different projects:
- I removed the ProductService class and IProductService interface and used the IProductRepository interface directly from the StockService class (Core project) and the /products endpoint (Web project: Program.cs).
- I removed the IStockService interface, and now both the add and remove stocks endpoints (Web project: Program.cs) depend directly on the StockService class.
Why use the IProductRepository interface directly, you might wonder? Since the Web project (infrastructure layer) depends on the core layer, we can leverage the inward dependency flow. It is acceptable to use a repository directly as long as the feature has no business logic. Programming empty shells and pass-through services adds useless complexity. However, when business logic starts to be involved, create a service or any other domain entity you deem necessary for that scenario. Don’t pack business logic into your controllers or minimal API delegates.I removed the IStockService interface since the StockService class contains concrete business rules that can be consumed as is from the infrastructure layer. I know we have emphasized using interfaces since the beginning of the book, but I also often said that principles are not laws. All in all, there is nothing to abstract away: if the business rules change, the old ones won’t be needed anymore. On the other hand, we could have kept the interface.To wrap this up, Clean Architecture is a proven pattern for building applications that is fundamentally an evolution of layering. Many variants can help you manage use cases, entities, and infrastructure; however, we will not cover those here. There are many open-source projects to start with Clean Architecture if you seek organizational guidance.
I left a few links in the Further reading section.
If you think this is a great fit for you, your team, your project, or your organization, feel free to dig deeper and adopt this pattern. In subsequent chapters, we explore some patterns, such as CQRS, Publish-Subscribe, and feature-based design, which we can combine with Clean Architecture to add flexibility and robustness. These become particularly useful as your system grows in size and complexity.