Reputation: 75
I'm relatively new to clean architecture and I have some questions about using CA for backend spring project.
Does the application must(is it a good practice to CA) be modularized or can it be one module split in folders?
About the Business Logic, should it be only on Usecases?
Repository
DTOs
Upvotes: 5
Views: 4383
Reputation: 2435
The architecture is the architecture, folders and modules are not architecture, they are trade offs. Modules may help design it so the business layers cannot access things like jdbc, at the cost of more module definitions.
The bridge (e.g. interfaces and boundary classes) is full of demands. The use cases are very demanding and can demand 5 or 10 different versions of the same operation. It may be expressed as en Enum or by 10 methods in the repository, such decisions are trade offs/style/etc.
Controllers would validate that the request is valid (think of them as a bank note checker in a supermarket). But the business logic (the POS) will validate if the coupon is valid and what the % off the price will be.
The repository to the coin dispenser can decide if it should dispense many small coins and perhaps in what currency. The coin dispenser can validate that it has enough coins in the machine.
Different layers have different validations. In a typed language, many validations are automated e.g. you declare a UUID and then it will be an error at runtime if it's in fact not.
Normally, Department and User are directly mapped from your mental model of the world, but that is not the best way when you design the software.
The use case should perhaps operate on NewDepartment and NewDepartment repository etc. User and Department might not even exist in the software. Instead, 20 different combination of properties exist that change together. You might call them after the avengers or by something meaningful. Ubiquitous language should be respected, but when that does not apply "Thanos" can be a great name for a module or concept. Everyone knows what Thanos does in your project, it's the module that purges data or w/e. Or maybe you have "Welcome" as a concept for new departments etc. Again, try to harmonise with ubiquitous language and try to free yourself from the mental model of things that do not change together. E.g. customer status and customer name are usually changed at completely different times.
Each use case normally has a very well-defined bridge, and thus a specialized repository.
Upvotes: 2
Reputation: 51353
Does the application must be modularized or can it be one module splitted in folders?
I can be modularized, but it must not. Modularization has benefits in compiled languages like Java or C++, because you can not introduce cycles between modules. E.g. if in Java a JAR B depends on JAR A, you can not introduce a dependency from JAR A to JAR B, because this would introduce a cycle and then JAR A could not be build until JAR B is build and vice versa.
Splitting into folders, packages or namespaces (depending on how your language names it) can make sense depending on your needs. Take a look at my answer to the question Who must create services with package modifier if I implement architecture from “Clean Architecture” book.
About the Business Logic, should it be only on Usecases?
All business rules should be placed in the use case layer or below, because the goal of the clean architecture is to make this part easy to test. The application specific business logic should be placed in the use cases, the application agnostic rules in the entities.
About inputs, could it be be on controllers or in domain(request) it self?
Separate the controller specific validation from the business validations. The controller specific should be placed at the controller level and the business specific in the use case and/or entity layer.
E.g. if you have a rest controller that consumes JSON and this JSON contains a date string. Then it is the responsibility of the controller to parse the string to a date object, e.g. as ISO date or maybe it tries to parse different formats. When you pass the parsed date object to the use case it is the responsibility of the use case to validate if this date is valid in the context of the use case. E.g. if you have a use case "ScheduleMeeting" the date should be in the future.
The use case layer should throws business exceptions like ScheduledTimeElapsedException
. The controller layer can catch this exception and decide what to do, e.g. an rest service might return a HTTP status code and/or a JSON with a more detailed description, maybe a internationalized description depending on the language settings of the requesting client.
Repository
Is a repository a gateway? if so which layer it fits?
A repository is an interface that the use case needs to get entities from and to put entities into.
A gateway is the implementation that knows how to contact an external service or a database to get data from and store data to.
Let's suppose I have two entities on DB, Department and Employee. The business rule is: "to create a Department I MUST create its employees too, and only through Department". In this situation what should I do?
Should I create two repositories and as I can only create employees when I create the Department, should I call the create() from Employee's repository through the Department repository?
You should apply the interface segregation principle and create a use case specific repository interface. This interface should only define the methods that the use case needs. Since the business rule is to create both, this interface could look like this.
public void MyUseCaseRepository {
public void persistDepartment(Department dep, List<Employee> employees);
}
If you apply the interface segregation principle the following other question should be answered too.
Should I create just one Usecase and create a gateway to orchestrate the repositories?
The implementation can then take responsibility for storing both or none.
You might think now "But then I move business logic into the gateway.". Read my answer to the question Should Repositories Throw Domain Errors.
Also about repository, can I call a Usecase through a repository? This makes me very confused, because theorically, the Usecase and the repository will have interfaces, and its implementations could be on the same layer, so is there any rule about it?
The clean architecture does not really give a statement about this. The repository implementations are located in the interface adapters layer like the controllers are. Thus theoretically they can access the use cases. But I think this is not the intention of the author. I think that Uncle Bob sees it more like the hexagonal architecture and the repositories are adapters while the controllers are ports. I would not call a use case from a repository implementation. Finally this question can only be answered by the author.
DTOs
May it have a second constructor expecting a "Input" and this constructor fill the respective fields?
I guess you mean a ResponseModel
that is returned by the use case when you say "Input".
From an architectural point of view this is ok, because the DTO belongs to the controller or interface adapters layer and thus the dependency direction is correct.
But when the DTO's constructor reads the response model and fills it's fields, it also converts the response model's values to their DTO representation. Maybe you want to separate this conversion from the DTO itself and place it in a presenter implementation that can be tested in isolation.
Is there any rule if I have to create three "layers" of domains? Like DepartmentRequest, Department and DepartmentEntity? If not can I have only have two or one object for an entity?
You can see the rule in the clean architecture. The dependency arrows start at an outer layer and end on the next inner layer. There is no arrow that point e.g. from the controllers layer to the entities layer. Thus controllers should not access entities if you are very strict with the architecture.
If you put everything in one object, e.g. Department
and you use this for almost everything, e.g. as DTO, as database entity and domain entity then you couple all these different responsibilities.
As a consequense you can easily break the DTO representation if you modify the way it is stored in the datase and vice versa. You can also easily break the database representation if you want to modify business rules and vice versa. And finally you can also break the DTO representation if you modify the business rules.
These objects look very similar, but they have different responsibilities because they serve different clients. Thus if you put everything in one object you will violate the single responsibility rule.
I can not tell you all the effects of a single responibility violation, because this goes far beyond this question.
Upvotes: 8