.Net core 3.1
Overview¶
La arquitectura de software que se explicara en este apartado fue adaptada a partir del siguiente repositorio : https://github.com/jasontaylordev/CleanArchitecture
Arquitectura¶
Para el desarrollo de un microservicio web api se desarrollo el siguiente árbol de directorios
├───Application
│ ├───Boopstrap
│ ├───Common
│ │ ├───Interfaces
│ │ └───Models
│ └───UseCase
│ ├───V1
│ │ ├───PersonOperation
│ │ │ ├───Commands
│ │ │ │ ├───Create
│ │ │ │ └───Update
│ │ │ └───Queries
│ │ │ └───GetList
│ │ └───WeatherForecasts
│ │ └───Queries
│ │ └───GetWeatherForecasts
│ └───V2
├───Domain
│ ├───Common
│ ├───Entities
│ ├───Enums
│ ├───Exceptions
│ └───ValueObjects
├───Infrastructure
│ ├───Boopstrap
│ ├───EventHandler
│ ├───Migrations
│ ├───Persistence
│ │ └───Configurations
│ └───Services
└───WebApi
├───Controllers
│ ├───V1
│ └───V2
├───Logs
├───Models
Domain¶
├───Common
├───Entities
├───Enums
├───Exceptions
└───ValueObjects
Decidimos dividir en estos directorios. Quizás los más relevantes a detallar sean:
- Entities: almacena las clases que se persisten en base de datos.
- ValueObjects: Objetos requeridos para transportar información y/o usar en la lógica del negocio.
Application¶
├───Boopstrap
├───Common
│ ├───Interfaces
│ └───Models
└───UseCase
├───V1
│ ├───PersonOperation
│ │ ├───Commands
│ │ │ ├───Create
│ │ │ └───Update
│ │ └───Queries
│ │ └───GetList
│ └───WeatherForecasts
│ └───Queries
│ └───GetWeatherForecasts
└───V2
La Capa de Application, tiene la responsabilidad de contener la lógica de negocio, interacciona con Domain para utilizar sus clases. Esta capa no debe tener dependencias ni lógica con recursos externos (Base de datos, micro services, bus event, etc). Para interactuar con el exterior esta capa utiliza el Principio de Inversión de Dependencias de SOLID.
Podemos explicar el objetivo de los directorios:
- Boopstrap: Directorio de configuración de dependencias, es donde agregaremos la configuración de las dependencias que deseamos inyectar.
- Common: Directorio para almacenar clases comunes, ejemplos services (que sólo interaccionan con el dominio), interfaces, modelos, excepciones, helpers, etc.
- UseCase: Es el directorio principal de application, es donde almacenaremos todos los casos de uso de nuestra aplicación. Sugerimos dividir por versión y utilizado el patrón CQRS.
Infraestructure¶
├───Boopstrap
├───EventHandler
├───Migrations
├───Persistence
│ └───Configurations
└───Services
- Persistencia: Carpeta donde almacenaremos la interacción con la base de datos. Repositories, Queries, DbContext.
Web API¶
├───Controllers
│ ├───V1
│ └───V2
├───Logs
├───Models
└───Properties
Como manejar un request¶
Para la nueva arquitectura, utilizamos asp.net
y MediatR
. Por lo que para manejar un request simplemente debemos realizar los siguientes pasos:
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class PersonController : ControllerBase
{
private readonly IMediator _mediator;
public PersonController(IMediator mediator)
{
_mediator = mediator;
}
}
IMediator
[HttpGet]
public async Task<IActionResult> Get() => this.Result(await _mediator.Send(new ListPerson()));
Send()
de mediator donde recibe un objeto de tipo IRequest
.
Creamos la clase
public class ListPerson : IRequest<Response<List<PersonDto>>>
{
}
public class ListPersonHandler : IRequestHandler<ListPerson, Response<List<PersonDto>>>
{
private readonly IReadOnlyQuery _query;
public ListPersonHandler(IReadOnlyQuery query)
{
_query = query;
}
public async Task<Response<List<PersonDto>>> Handle(ListPerson request, CancellationToken cancellationToken)
{
// ... handler request
}
}
Dentro de la función Handle
desarrollamos la lógica de negocio.
MediatR¶
La librería MediatR permite implementar el patrón Mediator de los patrones de diseño de GOF.
El patrón de diseño Mediator define un objeto que encapsula cómo interactúa un conjunto de objetos. Mediator promueve el acoplamiento flexible al evitar que los objetos se refieran entre sí de forma explícita y le permite variar su interacción de forma independiente.
Como se usa la librería¶
Básicamente para implementar la librería tenemos 2 tipos de objeto, el request y un Handler. El request es, en principio, el objeto que transporta el mensaje mientras que el handler es aquel que atiende ese mensaje, procesa y devuelve un resultado.
La interface IMediator
dependiendo el objeto IRequest
que se le provea, ejecutará el IRequestHandler
configurado.
Cómo configurar:
Para crear un request, simplemente debemos implementar la interface IRequest<TResponse>
donde TResponse
es el tipo devuelto de ese request.
en el siguiente ejemplo el objeto que se devuelve es una lista de PersonDto
public class ListPerson : IRequest<List<PersonDto>>
{
}
Para crear un Handler debemos crear un objeto que implemente la interface IRequestHandler<IRequest, TResponse>
donde debemos declarar cual es el request que espera y cual es la respuesta de ese request
NOTA La respuesta debe ser la misma que el declarado en el request.
public class ListPersonHandler : IRequestHandler<ListPerson, List<PersonDto>>
{
}
La ventaja de usar el mediator es que podemos definir al Handler las dependencias que necesitemos y el mediator se las inyectará cuando sea necesario.
La interface IRequestHandler
necesita que implementemos el método Handler
que es el que se ejecutará cuando llegue un mensaje.
public async Task<List<PersonDto>> Handle(ListPerson request, CancellationToken cancellationToken)
{
// ... handler request
}
IRequest
que configuramos y devuelve el TResponse
que definimos.
Para ejecutar esto, simplemente debemos inyectar la interface IMediator
y ejecutar el método Send(IRequest)
await _mediator.Send(new ListPerson());