Often developers, when talking about architecture, actually think about their project structure. Clean, Ports & Adapters, Onion... all of these are referring on how to organize your project. You either "inherit" it from your team or try some that makes more sense to you on new project or when refactoring. Thing is that project organization is important when you have a monolith or some coarse grained services that contain several functionalities that are somewhat related, but you want to keep them separate on code level and not to jump into making a (micro)service for each.
You usually end up with a solution that has several projects:
- API - just for controllers and to expose business logic
- Domain/Core - with business logic
- Infrastructure - interface implementation
- Persistence? - some folks like to keep DB stuff separated
So what is wrong with this structure? Listen to this talk by Simon Brown and continue reading to grab some code samples.
Your code doesn't match your diagram
Let's start by having an API that has two functionalities:
- Weather Forecast
- Calculator
How would you draw a component diagram? Something like this, right?
How would you structure your code? I guess something like this:
- API (project)
- Domain (project)
- WeatherForecast (folder)
- Calculator (folder)
- Infrastructure
- WeatherForecast (folder)
- Calculator (folder)
There is an obvious mismatch, right? You might say - that doesn't matter, we have a nice folder structure so we keep things separated and well organized.
OK, but what is stopping something from WeatherForecast
folder to call something else from Calculator
folder? Internal conventions and rules? Pull requests? Yes, all of that might help, but it would be more obvious if in that pull request you would see that unwanted relation trying to creeping in.
Then how to structure?
A suggestion is that it matches you component diagram, like this:
- API (project)
- WeatherForecast (project)
- ...
- Calculator (project)
- ...
I deliberately put three dots, since it is up to developer of the specific functionality how to organize code inside of it. Should you have Domain/Infrastructure/whatever folder structure in each project? Or you will have some convention that if functionality is simple and you have just few classes, you do not even want to have any sub-folder? Whatever your teams' convention is, that is fine.
Some obvious advantages are:
- Solution structure matches diagram, so it is obvious where the code is.
- All code related to one functionality is in one project, making it much harder to make some dangerous dependencies between different functionalities.
Encapsulate your component
We all know what is encapsulation, but we often think of it just in the terms of class encapsulation. Simon Brown suggested we do the encapsulation on component level too. If you think of your REST API, it is encapsulated as a consumer can only call exposed methods in a way you specified. Why not do the same for your component?
You might say you already do that by having interfaces that are then injected, so a caller can't access the implementation, but that is more often not true. Usually we have that API/Core/Implementation project structure and all our classes are public
. We need them to be public
as DI setup is usually in API project and they need to be visible.
With the new project-per-component structure, we do not have that problem any more. Implementation classes can be private
or internal
and therefore hidden from other components. Yes, you would have your DI configuration inside the component!
Sample solution
Grab a look at the sample solution on GutHub
nikolic-bojan / modularity
Sample solution showing how to organize code in Components/Modules, so we completely avoid layered approach.
Your "Clean Architecture" is still layered!
Often developers, when talking about architecture, actually think about their project structure. Hexagonal, Clean, Ports & Adapters, Onion... all of these are referring on how to organize your project. You either "inherit" it from your team or try some that makes more sense to you on new project or when refactoring. Thing is that project organization is important when you have a monolith or some coarse grained services that contain several functionalities that are somewhat related, but you want to keep them separate on code level and not to jump into making a (micro)service for each.
You usually end up with a solution that has several projects:
- API - just for controllers and to expose business logic
- Domain/Core - with business logic
- Infrastructure - interface implementation
- Persistence? - some folks like to keep DB stuff separated
So what is wrong with this structure? Listen…
Structure of my project is:
- API (project)
- Components (solution folder to group components)
- WeatherForecast (project)
- Calculator (project)
Things that are exposed from component are:
- Interfaces
- DTOs needed for interfaces
- Dependency Injection configuration
In API's Startup.cs
you just add calls to extension methods in order to configure DI like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddComponentWeatherForecast();
services.AddComponentCalculator();
}
Here is a sample of one extension method:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddComponentWeatherForecast(this IServiceCollection services)
{
return services.AddScoped<IWeatherForecastService, WeatherForecastService>();
}
}
Final words
I hope you like these encapsulated components. They are really making hard for team members to make "shortcuts". If components need to talk to each other, that is totally fine - only thing you can access on another component are interfaces and DTOs. If you want to make a full-blown service out of your component, that would be super-easy as everything is in one place.
Would like to hear your comments and questions.
Thanks for reading!
Top comments (7)
If nothing else I love the extension methods approach. Really cleans up the Startup class. Thank you.
You are welcome Malcolm.
Few months back I stumbled upon this YT channel from Derek where he, among other things. talks in short videos about Loosely coupled monolith youtube.com/playlist?list=PLThyvG1... and also gives his opinion on Clean architecture youtube.com/watch?v=Ys_W6MyWOCw
Thanks for these Bojan, finally got round to watching a few of these videos. Certainly given me some food for thought!
This is something like Orchard core provides for MVC type of project.. but I like this article where you shown how we can organise things in our manner and it seems to be more suitable for APIs without any additional framework involved..
This can be used when you are starting fresh, but also useful if you are doing refactoring and trying to modularize your monolith or a bigger service. There is another interesting video here youtube.com/watch?v=BOvxJaklcr0, also referring to Java, so you should take the conceptual part.
The main thing Simon Brown is trying to point out is that we should visualize our systems (he created C4 model) and that our implementation should match diagrams as much as possible.
Not to forget - we should design our systems :)
youtube.com/watch?v=qO73yObPYac
Like the idea of components, and the way you set them up using the extension methods in startup.cs.
Thank you Robin! Please let me know if you find some downsides of this approach.