Entonces, después de leer "Implementando el diseño impulsado por el dominio por Vaughn Vernon", he decidido refactorizar mi código para una mejor usabilidad al aislar lo que creo que son conceptos básicos del dominio en módulos separados.
Cada módulo contiene su propio conjunto de capas arquitectónicas distintas que incluyen el dominio, la infraestructura y la capa de aplicación / presentación (según la recomendación de Vaughn, he decidido separar aún más las responsabilidades de la capa de aplicación de las rutas, controladores MVC + plantillas que existen en el Capa de presentación).
He decidido colocar cada una de estas capas dentro de su propio paquete; y cada paquete hace referencia a la capa debajo de él como una dependencia. es decir: la capa de presentación depende de la capa de aplicación, la aplicación depende de la infraestructura, etc. Dado que el repositorio es parte del dominio, cada interfaz de repositorio existe dentro de la capa / paquete del dominio y la implementación es responsabilidad de la capa / paquete de infraestructura (Doctrine , etc.)
Espero que al reestructurar mi código de esta manera pueda intercambiar la capa de Aplicación y reutilizar mi Dominio en múltiples Aplicaciones Web.
El código finalmente parece que está empezando a formarse nuevamente, sin embargo, lo que aún me confunde es esta distinción entre Aplicación, Infraestructura y Servicios de dominio.
Un ejemplo común de un Servicio de dominio es algo que usaría para hacer hash de contraseñas. Esto tiene sentido para mí desde la perspectiva de SRP, ya que la entidad User no debería preocuparse por los diferentes algoritmos de hash que podrían usarse para almacenar las credenciales de un usuario.
Con eso en mente, traté este nuevo servicio de Dominio de la misma manera que mis Repositorios; definiendo una interfaz en el Dominio y dejando la implementación a la capa de Infraestructura. Sin embargo, ahora me pregunto qué debería hacerse con los Servicios de aplicación.
Tal como está ahora, cada entidad tiene su propio servicio de aplicación, es decir, la entidad de usuario tiene un servicio de usuario dentro de la capa de aplicación. El UserService en este caso es responsable de analizar los tipos de datos primitivos y manejar un caso de uso común "UserService :: CreateUser (nombre de cadena, cadena de correo electrónico, etc.): Usuario.
Lo que me preocupa es el hecho de que tendré que volver a implementar esta lógica en varias aplicaciones si decidiera cambiar la capa de aplicación. Así que supongo que esto me lleva a mis próximas preguntas:
¿Los servicios de dominio son simplemente una interfaz que existe para proporcionar una capa de abstracción entre la capa de infraestructura y su modelo? es decir: repositorios + servicios de hash, etc.
Mencioné tener un Servicio de Aplicación que se ve así:
Access / Application / Services / UserService :: CreateUser (nombre de cadena, cadena de correo electrónico, etc.): Usuario
La firma del método acepta argumentos de tipo de datos primitivos y devuelve una nueva entidad de usuario (¡no un DTO!).
¿Pertenece esto a la capa de Infraestructura como una implementación de alguna interfaz definida dentro de la capa de Dominio o la Capa de Aplicación es de hecho más apropiada debido a los argumentos de tipo de datos primitivos, etc. ?
ejemplo:
Access/Domain/Services/UserServiceInterface
y
Access/Infrastructure/Services/UserService implements UserServiceInterface
Cómo deben manejar los módulos separados las relaciones unidireccionales. ¿Debería el módulo A hacer referencia a la capa de aplicación del módulo B (como lo está haciendo ahora) o la implementación de la infraestructura (a través de una interfaz separada)?
¿Los servicios de capa de aplicación requieren una interfaz separada? Si la respuesta es sí, ¿dónde deberían ubicarse?
fuente
Respuestas:
Las responsabilidades de los servicios de dominio incluyen varias cosas. Lo más obvio es la lógica de la vivienda que no cabe en una sola entidad. Por ejemplo, es posible que deba autorizar un reembolso por una compra determinada, pero para completar la operación necesita datos de la
Purchase
entidad,Customer
entidad,CustomerMembership
entidad.Los servicios de dominio también pueden proporcionar las operaciones que necesita el dominio para completar su funcionalidad, por ejemplo
PasswordEncryptionService
, pero la implementación de este servicio residirá en la capa de infraestructura, ya que será principalmente una solución técnica.Los servicios de infraestructura son servicios que, por lo tanto, una operación de infraestructura, como abrir una conexión de red, copiar archivos del sistema de archivos, hablar con un servicio web externo o hablar con una base de datos.
Los servicios de aplicación son la implementación de un caso de uso en la aplicación que está creando. Si cancela una reserva de vuelo, debería:
La capa de aplicación es el cliente del dominio. El dominio no tiene idea de cuál es su caso de uso. Simplemente expone la funcionalidad a través de sus agregados y servicios de dominio. Sin embargo, la capa de aplicación refleja lo que está tratando de lograr al orquestar las capas de dominio e infraestructura.
PHP podría no ser el mejor lugar para comenzar a aprender sobre DDD ya que muchos de los frameworks PHP (Laravel, Symfony, Zend, etc.) tienden a promover RAD. Se centran más en CRUD y en traducir formas a entidades. CRUDO! = DDD
Su capa de presentación debe ser responsable de leer las entradas del formulario del objeto de solicitud e invocar el servicio de aplicación correcto. El servicio de aplicación creará el usuario e invocará el repositorio de usuarios para guardar al nuevo usuario. Opcionalmente, puede devolver un DTO del usuario a la capa de presentación.
El módulo de palabras en la jerga DDD tiene un significado diferente al que está describiendo. Un módulo debe contener conceptos relacionados. Por ejemplo, un módulo de pedido en la capa de dominio podría albergar el agregado de pedido, la entidad OrderItem, OrderRepositoryInterface y MaxOrderValidationService.
Un módulo Order en la capa de aplicación podría albergar OrderApplicationServie, CreateOrderCommand y OrderDto.
Si está hablando de capas, cada capa debe depender preferiblemente de las interfaces de otras capas siempre que sea posible. La capa de presentación debe depender de las interfaces de la capa de aplicación. La capa de aplicación debe hacer referencia a las interfaces de los repositorios o servicios de dominio.
Personalmente, no creo interfaces para entidades y objetos de valor porque creo que las interfaces deberían estar relacionadas con un comportamiento, pero YMMV :)
Depende :) para aplicaciones complejas, construyo interfaces porque aplicamos pruebas rigurosas de unidad, integración y aceptación. El acoplamiento flojo es clave aquí y las interfaces están en la misma capa (capa de aplicación).
Para una aplicación simple, construyo directamente contra los servicios de la aplicación.
fuente
Hmm respuesta corta a una pregunta larga, pero veo el patrón de la siguiente manera
Regla 1: los objetos de dominio deben tener una única raíz agregada
Regla 2: las raíces agregadas no deberían ser demasiado grandes, divida las cosas en contextos limitados
Problema: las raíces agregadas pronto se vuelven demasiado grandes y no hay una forma clara de trazar una línea entre los distintos modelos de dominio en ellas
Solución: Servicios de dominio. Cree interfaces que pueda inyectar en modelos de dominio que les permitan hacer cosas fuera de su contexto de dominio o raíz agregada.
Así que creo que diría que sus ejemplos son servicios / repositorios normales, etc., IDatabaseRepositoryForStoringUsers o IGenericHashingCode
Un servicio de dominio permite la comunicación entre contextos limitados. es decir
Donde los usuarios y las cuentas están en raíces agregadas / contextos limitados separados.
Si el usuario y la cuenta están en la misma raíz agregada, por supuesto, debería poder hacer:
No estoy totalmente claro de su pregunta sobre cómo está integrando el material de la Aplicación / Infraestructura nTier y el material del Módulo. Obvias que realmente no quieres ninguna referencia cruzada entre contextos limitados, por lo que colocarías tus interfaces de servicio de dominio en su propio módulo que no hace referencia a ningún otro contexto limitado. limitarlo a exponer tipos de valores base, o tal vez solo DTO
fuente