Clear architecture (onion architecture, hexagonal architecture) - josuamanuel/softwareDevelopmentPrinciples GitHub Wiki

image image

( For more extensive paradigms see computer folder: /⁨Users⁩/⁨e1182086⁩/⁨Documents⁩/⁨development⁩/⁨TricksAndKnowledge/cleanCode ⁩)

The diagram with boxes shows a typical scenario for a web-based Java system using a database. The web server (express route in node) gathers input data from the user and hands it to the Controller on the upper left. The Controller packages that data into a plain old Java object and passes this object through the InputBoundary to the Usecaselnteractor. The UseCaseInteractor interprets that data and uses it to control the dance of the Entities. It calls DataAccessInterface to search by key fields. DataAccess (implementation of DataAccessInterface) will recover from storage and create a new Entity that will be returned to UseCaseInteractor. DataAccess implementation is part of the Framework circle so we can use propietary SQL to access storage. To add in database, use Case will create a new Entity that will be passed as parameter to the DataAccessInterface. The UseCaseInteractor after completion of the Entity - DataAccess dance, will gathers data from the Entities and constructs the outputData as another plain old Java object. The outputData is then passed through the OutputBoundary interface to the Presenter.

The job of the Presenter is to repackage the outputData into viewable form as the ViewModel, which is yet another plain old Java object. The ViewModel contains mostly Strings and flags that the View uses to display the data. Whereas the outputData may contain Date objects, the Presenter will load the ViewModel with corresponding Strings already formatted properly for the user. The same is true of Currency objects or any other business-related data. Button and Menultem names are placed in the ViewModel, as are flags that tell the View whether those Buttons and Menultems should be gray. This leaves the View with almost nothing to do other than to move the data from the ViewModel into the HTML page. Note the directions of the dependencies. All dependencies cross the boundary lines pointing inward, following the Dependency Rule.

Observe how interfaces and Data structures are defined inside of the Use Case. This means the definition is imposed by the use case. It means we are in business domain territory. We must not contaminate these structures with infra semantic (http, statusCode, sqlcode, headers...).

Type of Interface adapters (callled Application layer aswell):

Defines the jobs the software is supposed to do by orchestrating the data flow from and to the domain models:

  • Controllers are the input from user to command something to the application.
  • Presenters are the response from the system to show the result of the command.
  • Gateways are external dependencies to cover the functionality. Example of gateways: Rest endPoints. database access. Triggering events.

// JMA nodeJs example with express and mongo.

Infraestructure (Framework & Drivers): External capabilities or Configuration and definition of this external capabilities. External to the source-code: • Chrome • Postman • Database Server (SQLServer) Specified in the source-code: • Jenkins pipeline deployment • config • express();app.listen(8080); • app.get('/customer/:custIID',getCustomerHandler) • mongoose.connect(uri, mongoOptions)

Adapters: Thin layer that comunicates and translate data in both directions betweeen Application and the infraestructure. Data coming in from infra can be contaminated with 3rd party flavour... but its goal is to create a simple plain objects to send to the use case. Data coming out from use case is transformed to generate the response that the infra needs. It is OK with Adapters creating entities that are returned to the useCase. • Controllers: * function getCustomerHandler(req, res, next) { try { customerEntity = getCustomerUseCase(req.param.custId) res.send(customerEntityToJSON(customerEntity)).status(200) }catach() {. res.send('error').status(500) } • Gateways: Data Respositories, rest end-points function getCustomerById(custId) { return customerEntity({customerID:custID, age, name} = ( await db .collection('contacts') .find({custId} ) ) } function fetchCustomerLangPreference(custId) { return (await fetch(https://..../custProfile/${custId})).lang }

Use Cases: Are invoked by a controller and it Orchestrates entities, calls to gateways, presenters... Use case represent the dynamism of the busines logic. The flow and orchestration to satisfy a need. This is opposite to Entities that are not attach to a specific flow, a specific case scenario. They define the logic in a static way so it can be reuse in multiple scenarios.

Entities: Domain logic that works for all use case... it is not affected by the type of user, the channel of execution. Entities define the intrinsic nature of the functionality that it is invariant.