Estoy construyendo una API Rest con la primavera de arranque y me gustaría aclarar un concepto sobre mi arquitectura, para ver si la comunidad me puede ayudar.
Imaginar que en mi base de datos tengo una tabla llamada Person
. Estoy montando una arquitectura basada en la arquitectura de tres capas. El esquema general sería la siguiente:
Por un lado tengo el
PersonDao.java
, que será la encargada de acceder a la base de datos para recuperar las tuplas de laPerson
tabla. Para ello, se utiliza una clase llamadaPersonEntity.java
, que contiene (como atributos) todas las columnas de la tabla.PersonDao.java
se va a devolver un objeto de laPersonModel.java
clase.PersonModel.java
es la clase que representa elPerson
modelo de negocio.Por otro lado, tengo el
PersonService.java
que se encarga de llevar a cabo la lógica de negocio de mi solicitud. Este servicio de llamadasPersonaDao.java
para acceder a la información almacenada en la base de datos.PersonService.java
trabaja con objetos de laPersonModel.java
clase, ya que esta clase representa los objetos de negocio de mi solicitud.PersonService.java
siempre devolverá unaPersonDto,java
.Por último,
PersonController.java
. Este controlador será la que expone la interfaz de conexión de la API Rest. Se trabaja siempre con DTO y se comunica conPersonService.java
a través DTO también.
PersonController <-> (PersonDto) <-> PersonService <-> (PersonModel) <-> PersonDao <-> (PersonEntity) <-> DB
La pregunta es: ¿Es necesario el uso de la PersonModel.java
clase para que PersonService.java
sólo funciona con objetos de esta clase? ¿O sería mejor para PersonService.java
trabajar directamente con los objetos de la clase PersonEntity.java
? Si lo hago de esta manera es mantener el principio de la responsabilidad individual, de manera que cada capa sólo funciona con objetos de su alcance.
Si la respuesta es que PersonModel.java
es necesario mantener el principio de la responsabilidad individual de cada capa. ¿Podría algo cambio, si JpaRepository
se utiliza? Si hago esta pregunta es porque en muchos tutoriales y ejemplos que veo que cuando JpaRepository
se utiliza, los servicios trabajan directamente con las entidades. En este caso, no deberíamos crear una clase que representa el objeto de negocio para los servicios?
EDITAR : En la respuesta a esta pregunta ( ? Entidades de primavera deben convertirse al Dto en el servicio ), la arquitectura que tiene sentido en mi cabeza se reflejaría, pero seguramente no es lo más correcto. La conclusión a la que llegamos es que cada capa utilizaría su propio tipo de objetos. Copiar / pegar de la respuesta:
Normalmente, usted tiene diferentes capas:
- Una capa de persistencia para almacenar datos
- capa para operar en los datos de negocio
- Una capa de presentación para exponer datos
Por lo general, cada capa utilizaría su propio tipo de objetos:
- La persistencia de la capa: Repositorios, Entidades
- Capa de negocio: Servicios, objetos de dominio
- Capa de Presentación: Controladores, dtos
Esto significa que cada capa sólo funcionaría con sus propios objetos y nunca pasan a otra capa.
Gracias por adelantado.
Por lo que entiendo su pregunta es sobre todo acerca de las capas y su Useage dentro del logic
nivel. (Así, el presentation
y data
los niveles no son parte de la pregunta).
Acerca de PersonModel
No estoy seguro, lo que quiere decir con el PersonModel
y lo que realmente hace, pero a primera vista se puede decir que normalmente no haría falta algo por el estilo, lo que sólo tiene que añadir la duplicación de código adicionales y actividades generales de mantenimiento, tarde o temprano.
Acerca de PersonDto
El DTO
s, como su nombre indica, son realmente destinado a ser para la transferencia de datos entre el cliente ( presentation
capa) y su API ( controller / boundary
capa dentro de la logic tier
) que se utilizan para exponer "amigable cliente" presentación de su modelo de dominio , para regular el exceso y bajo-ir a buscar - de alguna manera (gracias a GraphQL esto es ahora casi ya no es un problema). Por lo que sus clases de servicios empresariales no necesitan saber o hacer frente a los dtos en absoluto.
Además, como ya se ha mencionado, en aras de la SRP del negocio o Dao clases no deben tratar con el mapeo de datos adicionales (por ejemplo Dto <-> Modelo, Modelo <-> Entidad) de ninguna manera. Ellos ya cumplen una tarea determinada dentro de la capa de lógica (cf. capa límite , capa de servicio ).
Acerca de PersonEntity
Esto es algo que normalmente representa tanto la verdadera entidad de su dominio del problema y data
de nivel (por ejemplo, la base de datos en una tabla RDBMS o documento en un NoSQL). Así que es bastante común
que las clases de entidad se nombran sin sufijo como
Entity
. Sólo tiene que utilizar el nombre sencillo para ello, por ejemploPerson
,que las clases de entidad contienen anotaciones adicionales (por ejemplo JPA) para que sean visibles para el
ORM layer
(por ejemplo Hibernate),que las clases de entidad no deben ser necesariamente anémica y en realidad contienen un comportamiento addditional (probablemente lo que quería hacer con su
PersonModel
clase), por ejemplo,
class Person {
Date birthday;
int getAge() { /* calculate age based on the birthday */ }
boolean isAdult() { /* getAge() >= 18 */ }
}
Terminando
Caso de uso: creación de Person
la entidad
Hinflug (vuelo de ida)
[Client] --> (data: JSON) --> <Deserialization as `PersonDTO`> --> <DTO Validation> --> [PersonController] --> <AutoMapping to `Person` entity> --> [PersonService] --> [PersonDao] --> <ORM and JDBC> --> <Entity Validation> --> DB
Nota: <Validation>
también se puede hacer dentro del controlador manual, pero por lo general Bean Validation API se utiliza para automatizar este proceso en el fondo de color . Lo bueno es que se puede utilizar la API Bean Validation para validar tanto sus OTD y las entidades (por ejemplo, con Hibernate Validator ).
Vuelo de regreso (vuelo de vuelta)
DB --> <ORM and JDBC> --> [PersonDao] --> [PersonService] --> <AutoMapping to `Person` entity> --> [PersonController] --> <AutoMapping `Person` entity to `PersonDTO`> --> <Serialization of `PersonDTO`> --> (data: JSON) -> [Client]