Wanilton Filho
Wanilton Filho

Reputation: 75

Clean architecture boundaries

I'm relatively new to clean architecture and I have some questions about using CA for backend spring project.

  1. Does the application must(is it a good practice to CA) be modularized or can it be one module split in folders?

  2. About the Business Logic, should it be only on Usecases?

    Can I have "Validators" in any layer?
      e.g.
        Validations about size, nullable, optional fields could be it on InputRequest Objects? Or in Controller itself calling a PaginationValidator for example?
        Validation in repository checking if a field should or not be updated when I have the following Business Rule: "If the field is null, does not update, otherwise update it".
        If I have an Usecase calling a gateway, may the gateway also call a validator? Or the validation MUST happens in the Usecase?
  1. Repository

      Is a repository a gateway? if so which layer it fits?
      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?
        Should I Have two Usecases (one for creating department and one for creating employee) even if I can only create an Employee when creating a Department?
        If I have two Usecases, should I call the Employee Usecase through the Department Usecase or create another Usecase to orchestrate them?
        Should I create just one Usecase and create a gateway to orchestrate the repositories?
        Any other option?
      Also about the repository, can I call a Usecase through a repository? This makes me very confused, because theoretically, the Usecase and the repository will have interfaces, and its implementations could be on the same layer, but the arrows would be moving forwards and backward isn't?
  2. DTOs

      May it have a second constructor expecting a "Input" and this constructor fill the respective fields? e.g. Department having a constructor expecting aDepartmentRequest
      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?
      Should all conversion pass through an assembler/converter?


I already search on the internet, but all this information seems too abstract and sometimes ambiguous, if someone can give me an explanation with examples, I'll be appreciated! Thanks!!

Upvotes: 5

Views: 4383

Answers (2)

Karl Kildén
Karl Kildén

Reputation: 2435

  1. Think about Bob, would he not use this pattern if the software environment/Lang did not have a module concept?

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.

  1. Use cases are the public api of the application so the validation goes there. The use cases shall implement only a bridge from the bridge pattern and the other end of the bridge is e.g. a repository.

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.

  1. You are blending in abstractions from other patterns. A repository implements a bridge from the use-case. The demands from the use-case. In business applications, the public api in the repository is the sum of all bridges it is forced to implement. if the repository choses to have all the bridges it implements in an API layer or not is a trade-off.

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.

  1. Tradeoffs. Don't couple your public API to the database e.g. don't return a database tuple. Converting the tuple to an object is not changing this fact, so a minimum of one conversion is usually needed. When the business logic has a lot of code in it, then also a second conversion, to allow the database to change, is common.

Upvotes: 2

René Link
René Link

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.

enter image description here

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.

The clean architecture

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

Related Questions