Skip to content

Patterns recommendations and operations

Talysson de Oliveira Cassiano edited this page Mar 28, 2017 · 1 revision

As stated in Folder structure, the boilerplate follows an architecture inspired by DDD and Clean Architecture. It implies in some patterns and methodologies having to be followed to ensure separation of concerns and make the codebase more maintainable, I'll list of them here and suggest some links in the end with further info of patterns and practices that work well with this architecture.

Separation of concerns

Separation of concerns is ensured in this codebase in two ways:

  1. Separating the source code in layers, each layer with a different responsibility that should be followed;
  2. Each layer is also separated by actual concerns. When we talk about concerns in software development it's not about functionality, like controllers or models, but about concepts or contexts, like users, logging, persistence and so on.

All the patterns below have direct relation with separation of concerns.

Operations and use cases

The classes that represent the use cases of your application are called operations in the codebase, and they should be on the app layer. They are instances of EventEmitter, so they emit events for each of the possible outputs of the operation, like SUCCESS, ERROR, VALIDATION_ERROR and the like.

The operations should have no idea about the existence of the infra and interfaces layer, it should just be a class that receives its dependencies through the constructor (we use dependency injection for that) and have a method that executes the operation emitting events for its outputs. Implementing it like this makes it way easier to test and enabled you to use then in any entry point, just instantiating it in the entry point passing the required data and handling the events. You can see an example of that in the UsersController, that uses the CreateUser operation, it just takes the data from the requests and handle it to the CreateUser instance.

Repository pattern

Inside the use cases you should also not touch the database directly, it's not the responsibility of the application layer to know how to work with data persistence. So we implement repositories that handle the persistence internally and inject them on the operations instances.

Attention for this point: the repositories interfaces (as in OOP interfaces, not the interfaces layer) belongs to the domain layer, and their implementations (that effectively talk to the database) belongs to the infra layer, but since we don't have interfaces in JavaScript we just implement them on the infra layer and inject it with the name of the imaginary interface. An example of that is the UsersRepository, we use it in the operations like usersRepository, but what we are really injecting is the SequelizeUsersRepository that communicates internally with the SQL database. The important point here is that: The operation doesn't know how the repository works internally, it just knows the UsersRepository methods and the parameters it expects.

The repository implementations should also return something that the domain and the app layers can have access, so that's why we use mappers for that, that receives stuff from the database and convert it to objects from the domain layer. An example of that is the SequelizeUserMapper, that knows how to convert an record from the users table of the database to an instance of the User domain class and vice versa.

Separating the persistence from the app layer like this make it easier to test the app layer with different simulated responses from the database, including errors.

Further info

You can know more about the subjects that we talked about here in the following links: