Compartir a través de


Este artículo proviene de un motor de traducción automática.

Modelos de dominio

Empleando el modelo del modelo de dominio

Udi Dahan

En este artículo se describe:
  • Modelo del modelo de dominio
  • Escenarios para utilizar el modelo del modelo de dominio
  • Eventos de dominio
  • Mantener la empresa en el dominio
En este artículo usa las tecnologías siguientes:
Patrón del modelo de dominio

En este artículo, vamos a través de la emplean razones a (y no a) el patrón de modelo de dominio, las ventajas aporta, así como proporcionar algunos consejos prácticos en mantener la solución global tan sencillo como sea posible.

Contenido

¿Qué es IT?
Razones no se debe utilizar el modelo de dominio
Tecnología
Razones para utilizar el modelo de dominio
Escenarios para no utilizar el modelo de dominio
Escenarios para utilizar el modelo de dominio
Interacciones más complejas
Cutting entre BusinessRules
Eventos de dominio y sus llamadores
Eventos de dominio explícita
Capacidad de prueba
Comandos y consultas
Mantener la empresa en el dominio
Concurrencia
Buscar una solución completa
Works citados

Si tenía me hace unos años y solicita si nunca utiliza el modelo del modelo de dominio, podría han respondido con absoluta "Sí". Estaba seguro de mi comprensión de la trama. Tenía que admitir las tecnologías que funcione.

Pero, habría sido completamente incorrecto.

Mi descripción ha evolucionado a lo largo de los años y ha obtenido una apreciación de cómo una aplicación puede beneficiarse propio alinear con esos mismos principios controlado por dominio.

En este artículo, iremos a través de por qué nos incluso que desee tener en cuenta que emplean el modelo del modelo de dominio (así como por qué no), las ventajas que supone que se debe para poner, cómo interactúa con otras partes de una aplicación y las características nos gustaría desea proporcionar compatibilidad con tecnologías y explique algunos consejos prácticos en mantener la solución global tan sencillo como sea posible.

¿Qué es IT?

El autor del patrón de modelo de dominio, Martin Fowler, proporciona esta definición (Fowler, 2003):

Un modelo de objetos del dominio que incorpora comportamiento y datos.

A decir la verdad, se puede interpretar esta definición para ajustarse a casi cualquier fragmento de código, una bastante buena razón por qué pensé que estaba utilizando el modelo cuando de hecho no se true a su intención original.

Vamos a profundizar.

Razones no se debe utilizar el modelo de dominio

En el texto siga la descripción original, tenía originalmente ha pasado este pasaje inocua, pero resulta que muchas decisiones importantes hinge en comprenderlo.

Dado que el comportamiento de la empresa está sujeto a gran cantidad de cambio, es importante poder modificar, crear y probar fácilmente esta capa. Como resultado deseará que el mínimo de acoplamiento desde el modelo de dominio a otras capas en el sistema.

Por lo que una razón no desea que el uso del modelo del modelo de dominio es si la empresa que es automatizar su software no cambia mucho. Eso no quiere para decir no cambia en absoluto, pero en lugar de las reglas subyacentes dicta cómo se realiza la empresa no muy dinámicas. Aunque pueden cambiar otros factores ambientales y tecnológicas, no es el contexto de este modelo.

Algunos ejemplos de esto incluyen compatibilidad con varias bases de datos (como SQL Server y Oracle) o varias tecnologías de interfaz de usuario (Windows, Web, móviles y así sucesivamente). Si no ha cambiado el comportamiento de la empresa, estos no justificar el uso del modelo del modelo de dominio. No es decir uno podría no obtener una gran tratar del valor del uso de tecnologías que admiten el modelo, pero deben ser honesto acerca de qué reglas se interrumpir y por qué.

Razones para utilizar el modelo de dominio

En esos casos donde el comportamiento de la empresa es sujeto gran cantidad de cambio, tener un modelo de dominio se reducirá el costo total de esos cambios. Tener todo el comportamiento de la empresa que es probable que cambie encapsulados en una parte única de nuestro software se reduce la cantidad de tiempo que es necesario realizar un cambio porque todo se realizará en un lugar. Al aislar ese código tanto como sea posible, podemos reducir la probabilidad de cambios en otros lugares que se interrumpe, lo que reduce el tiempo que tarda para estabilizar el sistema.

Escenarios para no utilizar el modelo de dominio

Esto nos conduce al no. 1 fallacy más comunes acerca de cómo emplear modelos de dominio. Que yo mismo fue culpa de realizar esta suposición es false para un número de años y se vea ahora donde condujo me astray.

Fallacy: Cualquier modelo de objetos persistentes es un modelo de dominio

En primer lugar, un modelo de objeto persistente no encapsula intrínsecamente todos los comportamientos de la empresa es probable que cambien. En segundo lugar, un modelo de objetos persistentes puede incluir funciones que no es probable que cambie.

La naturaleza de esta fallacy es similar al que indica que cualquier destornillador es un martillo. Aunque puede martillo (intente) en clavos con un destornillador, no será muy eficaz de hacerlo de esta forma. Uno se puede expresar que estaban siendo true al modelo de martillo.

Poner este volver concretas escenarios que todos conocemos y amor, consideremos el requisito de constante que dirección de correo electrónico de un usuario debe ser única.

Durante un tiempo, pensé que el objetivo de tener un modelo de dominio era que requisitos así podrían implementarse existe. Sin embargo, cuando consideramos la orientación que el modelo de dominio es sobre la captura los comportamientos de negocio que están sujetos a cambios, podemos ver que este requisito no cabe que mold. Es probable que este requisito no va a cambiar nunca.

Por lo tanto, decide implementar tal un requisito en la parte del sistema que se acerca de encapsular las partes de la empresa volátiles sentido, puede resultar difícil implementar y no podría realizar que bien. Poner todas las direcciones de correo electrónico en la memoria sería probablemente le bloqueado por la policía de rendimiento. Necesidad de llamar a algún servicio el modelo de dominio, que llama a la base de datos, para ver si la dirección de correo electrónico es innecesario. Una restricción única en la base de datos sería suficiente.

Este pensamiento pragmático es muy similar en el núcleo de la trama de modelo de dominio y el diseño controlado por dominio y qué simplificar las cosas tal como nos abordar los requisitos más complicados que la unicidad de correo electrónico simple.

Escenarios para utilizar el modelo de dominio

Reglas de negocio que indican cuando se permiten determinadas acciones son buenos candidatos para que se implementan en un modelo de dominio.

Por ejemplo, en un sistema de comercio electrónico una regla que indica que un cliente puede tener no más de 1.000 dólares en pedidos no pagados sería probablemente pertenecer en el modelo de dominio. Observe que esta regla implica varias entidades y tendría que evaluarse en una variedad de casos de uso.

Por supuesto, en un modelo dominio dado debería vamos a ver muchos de estos tipos de reglas, incluyendo casos donde algunas reglas reemplazan otras. En nuestro ejemplo anterior, si el usuario realizar un cambio en un pedido es el Administrador de cuenta de administrador para la cuenta pertenece el cliente, no se aplica la regla anterior.

Puede parecer innecesario dedicar tiempo a explicar por qué reglas necesitan aplicar en qué casos de uso y rápidamente idear una lista de entidades y relaciones entre ellas, eschewing el "big diseño por adelantado" que prácticas ágiles rail contra. Sin embargo, las reglas de negocio y casos de uso son las razones muy que se va a aplicar el modelo del modelo de dominio en primer lugar.

Cuando resolver estos tipos de problemas en el pasado, no ha pensado dos veces y se ha diseñado rápidamente una clase de cliente con una colección de objetos Order. Pero nuestras reglas indican hasta una sola propiedad de cliente en su lugar, UnpaidOrdersAmount. Se podría pasar por varias reglas y nunca se ejecutan en algo que apuntaba claramente a una colección de pedidos realmente. En este caso, el maxim ágil "no gonna necesita" (YAGNI) debe impedir que nos cree dicha colección.

Cuando se observa cómo conservar este gráfico de objetos, se puede resultar adecuado agregar objetos y colecciones debajo auxiliares. Necesitamos diferenciar claramente entre los detalles de implementación y comportamientos de empresariales principales que son responsabilidad del modelo de dominio.

Interacciones más complejas

Considere el requisito de que cuando un cliente ha realizado más de 10.000 $ merece la pena de compras con nuestra compañía, se convierten en un cliente "preferido". Cuando un cliente se convierte en un cliente preferido, el sistema debe envía un correo electrónico informándoles de las ventajas de nuestro programa de cliente preferido.

Lo que distingue esta situación del requisito de dirección de correo electrónico única que se ha descrito anteriormente es que esta interacción implica necesariamente el modelo de dominio. Una opción consiste en implementar esta lógica en el código que llama el modelo de dominio como sigue:

public void SubmitOrder(OrderData data)
{ 
   bool wasPreferredBefore = customer.IsPreferred;
   // call the domain model for regular order submit logic
   if (customer.IsPreferred && !wasPreferredBefore)
      // send email    
} 

Una dificultad que evita el código de ejemplo es la de comprobación de la cantidad que constituye cuando un cliente se convierte en preferido. Que la lógica es adecuadamente encargada del modelo de dominio.

Por desgracia, podemos ver que el código de ejemplo está sujeto al se recargan medida más reglas se agregan al sistema que debe evaluarse cuando se envían pedidos. Incluso si tuviéramos que mover este código en el modelo de dominio, se debería dejarse sigue los siguientes problemas.

Cutting entre BusinessRules

Puede haber otros casos de uso que el cliente preferido cada vez. No queremos tener que duplicar esa lógica en varios lugares (si está en el modelo de dominio o no), especialmente porque la refactorización para un método extraído todavía sería requieren capturar el estado preferido original del cliente.

Puede incluso necesitamos va hasta el momento como incluir algún tipo de método de (AOP) programación orientada a aspectos o interceptación para evitar la duplicación.

Parece mejor nos gustaría replantearse nuestro enfoque antes de nosotros nos Cortar en Segadora de Occam. Mirando nuestros requisitos nuevo puede enviarnos algunos dirección.

Cuando un cliente ha convertido en un [algo] el sistema debe [hacer algo].

Nos parece faltar una buena forma de representar este patrón requisito, aunque esto parecer algo que un modelo basado en eventos puede controlar bien. De este modo, si se tiene que hacer más en la "debe hacer algo" parte, se podría implementar fácilmente como controlador de eventos adicionales.

Eventos de dominio y sus llamadores

Eventos de dominio son la forma se representar explícitamente la primera parte de los requisitos descritos:

Cuando un [algo] ha convertido en un [algo]...

Mientras estos eventos se pueden implementar en las entidades a sí mismos, puede ser conveniente que ellos estar accesible en el nivel de todo el dominio. Vamos a comparar cómo se comporta la capa de servicio en cualquier caso:

public void SubmitOrder(OrderData data)
{ 
   var customer = GetCustomer(data.CustomerId);
   var sendEmail = delegate { /* send email */ };
   customer.BecamePreferred += sendEmail;
   // call the domain model for the rest of the regular order submit logic
   customer.BecamePreferred -= sendEmail; // to avoid leaking memory
} 

Aunque es bueno no tener que comprobar el estado antes y después de la llamada, se ha comercializado esa complejidad con el de suscripción y eliminación de suscripciones desde el evento de dominio. Además, no debería tener código que llama el modelo de dominio en cualquier caso de uso que saber si un cliente puede resultar preferido no existe. Cuando el código es interactuar directamente con el cliente, esto no es tal muy importante. Pero considere que, al enviar un pedido, se puede poner el inventario de uno de los productos del pedido su umbral de reposición, no deseamos controlar ese evento en el código, demasiado.

Sería mejor si podríamos tener cada evento se controla mediante una clase dedicada que no tratar cualquier caso de uso específico, pero podría activarse genéricamente como sea necesario en todos los casos de uso. Ésta es una clase como aspecto:

public class CustomerBecamePreferredHandler : Handles<CustomerBecamePreferred>
{ 
   public void Handle(CustomerBecamePreferred args)
   {
      // send email to args.Customer
   }
} 

Hablaremos acerca de qué tipo de infraestructura hará esta clase mágicamente obtener le llama cuando es necesario, pero veamos lo que queda del código de orden de envío original:

public void SubmitOrder(OrderData data)
{ 
   // call the domain model for regular order submit logic
} 

Eso como limpio y sencillo como uno podría esperar, nuestro código no necesita saber nada acerca de eventos.

Eventos de dominio explícita

En la clase CustomerBecamePreferredHandler vemos la referencia a un tipo denominado CustomerBecamePreferred: una representación explícita en el código de la aparición mencionada en el requisito. Esta clase puede ser tan sencilla como esto:

public class CustomerBecamePreferred : IDomainEvent
{ 
   public Customer Customer { get; set; }
} 

El siguiente paso es tienen la capacidad para cualquier clase dentro de nuestro modelo de dominio de provocar tal un evento, que se consigue fácilmente con la siguiente clase estática que hace uso de un contenedor como Unity, Castle o sesión:

public static class DomainEvents
{ 
   public IContainer Container { get; set; }
   public static void Raise<T>(T args) where T : IDomainEvent
   {
      foreach(var handler in Container.ResolveAll<Handles<T>>())
         handler.Handle(args);
   }
} 

Ahora, cualquier clase en nuestro modelo de dominio puede provocar un evento de dominio, con clases de entidad suele provocar los eventos así:

public class Customer
{ 
   public void DoSomething()
   {
      // regular logic (that also makes IsPreferred = true)
      DomainEvents.Raise(new CustomerBecamePreferred() { Customer = this });
   }
} 

Capacidad de prueba

Aunque la clase DomainEvents mostrada es funcional, puede realizar pruebas un modelo de dominio algo engorroso como nos gustaría necesario que utilice de un contenedor para comprobar ese dominio se provocan eventos. Algunas adiciones a la clase DomainEvents pueden paso adicional del problema, tal como se muestra en la figura 1 .

Figura 1 elementos a la clase DomainEvents

public static class DomainEvents
{ 
    [ThreadStatic] //so that each thread has its own callbacks
    private static List<Delegate> actions;

    public IContainer Container { get; set; } //as before

    //Registers a callback for the given domain event
    public static void Register<T>(Action<T> callback) where T : IDomainEvent
    {
       if (actions == null)
          actions = new List<Delegate>();

       actions.Add(callback);
   }

   //Clears callbacks passed to Register on the current thread
   public static void ClearCallbacks ()
   {
       actions = null;
   }

   //Raises the given domain event
   public static void Raise<T>(T args) where T : IDomainEvent
   {
      foreach(var handler in Container.ResolveAll<Handles<T>>())
         handler.Handle(args);

      if (actions != null)
          foreach (var action in actions)
              if (action is Action<T>)
                  ((Action<T>)action)(args);
   }
} 

Ahora se puede convertir una prueba unitaria completamente autocontenida sin necesidad de un contenedor, como Figura 2 se muestra.

Figura 2 prueba de unidades sin contenedor

   public class UnitTest
   {
        public void DoSomethingShouldMakeCustomerPreferred()
        {
            var c = new Customer();
            Customer preferred = null;

            DomainEvents.Register<CustomerBecamePreferred>(
                p => preferred = p.Customer
                    );

            c.DoSomething();
            Assert(preferred == c && c.IsPreferred);
        }
   }

Comandos y consultas

Todos los casos de uso que se ha ha examinando hasta han tratado con cambiar datos y las reglas a su alrededor. Aún en muchos sistemas, los usuarios también necesitarán poder ver estos datos, así como realizar a todo tipo de búsquedas, ordenaciones y filtros.

Originalmente había pensé que las mismas clases entidad que estaban en el modelo de dominio deben utilizarse para mostrar datos al usuario. En los años, he ha obtener utilizado para comprender que mi original pensando a menudo resulta para ser incorrecto. El modelo de dominio es todo acerca de encapsulación de datos con comportamientos de negocio.

Mostrar información de usuario no implica ningún comportamiento de negocio y es todo sobre cómo abrir los datos. Incluso cuando se tienen ciertos requisitos relacionados con la seguridad alrededor del cual los usuarios pueden ver qué información, que a menudo puede representarse como un filtrado obligatoria de los datos.

Mientras estaba "correcta" en el pasado en crear un modelo único objeto persistente que controla los comandos y consultas, a menudo era muy difícil de escala, como cada parte del sistema tugged el modelo en una dirección diferente.

Resulta que los desarrolladores realizar a menudo en más extenuantes requisitos que realmente necesita la empresa. La decisión de utilizar las entidades de modelo de dominio para mostrar información al usuario es sólo un ejemplo.

Verá, en un sistema multiusuario, realizados por un usuario no tienen que ser visible inmediatamente para todos los demás usuarios necesariamente. ¿Todos nos implícitamente comprender esto cuando presentamos caché para mejorar el rendimiento, pero permanecen las preguntas más profundas: si no necesita los datos más actualizados, por qué se vaya a través del modelo de dominio funciona necesariamente en los datos? ¿Si no necesita el comportamiento se encuentra en las clases del modelo de dominio y por qué plough a través de ellos para obtener sus datos?

Para esos antiguo suficientemente recordar, las prácticas recomendadas alrededor mediante COM + guiada nos para crear componentes independientes para lectura - sólo y lógica de lectura y escritura. Aquí estamos, más adelante, una década con nuevas tecnologías como Entity Framework, pero siguen manteniendo estos mismos principios.

Obteniendo datos de una base de datos y mostrar a un usuario es un problema bastante trivial para resolver estos días. Puede ser tan simple como utilizar un datos de ADO.NET lector o un conjunto de datos.

Figura 3 Muestra el aspecto podría tener nuestra arquitectura "nuevo".

fig03.gif

Figura 3 modelo para obtener datos desde una base de datos

Una cosa que en este modelo es diferente de enfoques típicos en función de enlace de datos bidireccional, es que no se utiliza la estructura que se utiliza para mostrar los datos para los cambios. Esto hace cosas como seguimiento de cambios no es completamente necesario.

En esta arquitectura, flujos de datos copia de la derecha de la base de datos para el usuario en el formulario de consultas y hacia abajo, en el lado izquierdo del usuario a la base de datos en el formulario de comandos. Elegir Ir a una base de datos totalmente independiente utilizada para las consultas es una opción atractiva en términos de rendimiento y escalabilidad, como lecturas no interfieran con escrituras de la base de datos (incluidos las páginas de datos se guardan en memoria en la base de datos), pero es necesario un mecanismo de sincronización explícita entre los dos. Las opciones para este incluyen servicios de sincronización de ADO.NET, SQL Server Integration Services (SSIS) y publicación/suscripción de mensajería. Elegir una de estas opciones está fuera del ámbito de este artículo.

Mantener la empresa en el dominio

Uno de los desafíos que enfrentan los desarrolladores al diseñar un modelo de dominio es cómo asegurarse de que no impresión a sangre lógica empresarial fuera del modelo de dominio. No hay ninguna solución de viñeta de plata para esto pero consigue un estilo de trabajo buscar un delicado equilibrio entre la simultaneidad, corrección y encapsulación de dominio que incluso puede probarse para con herramientas de análisis estático como FxCop.

Éste es un ejemplo del tipo de código que no deseamos ver a interactuar con un modelo de dominio:

public void SubmitOrder(OrderData data)
{ 
   var customer = GetCustomer(data.CustomerId);
   var shoppingCart = GetShoppingCart(data.CartId);
   if (customer.UnpaidOrdersAmount + shoppingCart.Total > Max)
      // fail (no discussion of exceptions vs returns codes here)
   else
      customer.Purchase(shoppingCart);
} 

Aunque este código es bastante orientado a objetos, podemos ver que una determinada cantidad de lógica empresarial se realiza aquí, en lugar de en el modelo de dominio. Un enfoque preferible sería esto:

public void SubmitOrder(OrderData data)
{ 
   var customer = GetCustomer(data.CustomerId);
   var shoppingCart = GetShoppingCart(data.CartId);
   customer.Purchase(shoppingCart);
} 

En el caso de nuevo pedido que excedan el límite de pedidos no pagados, que podría estar representado por un evento de dominio, que se controla mediante una clase independiente como se muestra anteriormente. El método de compra no provocaría que los cambios de datos en ese caso, lo que una transacción técnicamente correcta sin ningún efecto de negocio.

Al inspeccionar la diferencia entre los dos ejemplos de código, podemos ver que llamar a sólo un método único en el modelo de dominio, necesariamente significa que toda la lógica empresarial encapsular existe. La API más centrada del modelo de dominio a menudo mejora más capacidad de prueba.

Aunque esto es un buen paso en la dirección correcta, abrir hasta preguntas acerca de la concurrencia.

Concurrencia

Verá, entre el momento que obtenemos al cliente y el tiempo que podamos realizar la compra, otra transacción puede vienen en y cambiar al cliente de tal manera que se actualiza la cantidad de pedido no pagados. Pueden producirse nuestra transacción realizar la compra (basada en datos recuperadas previamente), aunque no cumpla con el estado actualizado.

La forma más sencilla para resolver este problema es para que nosotros hacer que se bloquean cuando se leyó originalmente el registro de cliente, realizado por el que indica un nivel de aislamiento de transacción de al menos lectura repetible (o serializable, que es el valor predeterminado) como sigue:

public void SubmitOrder(OrderData data)
{ 
   using (var scope = new TransactionScope(
   TransactionScopeOption.Required, 
   new TransactionOptions() { IsolationLevel = IsolationLevel.RepeatableRead }
))
   {
      // regular code
   } 
}

Aunque esto tarda un bloqueo ligeramente más caro que el nivel de aislamiento de lectura confirmada ha liquidado algunos entornos de alto rendimiento, puede mantener el rendimiento en niveles similares cuando las entidades de un caso de uso determinado se cargan concienzudamente y están conectadas mediante columnas indizadas. A menudo en gran medida se desplaza por mucho más sencillo modelo de codificación aplicable, ya que ningún código se requiere para identificar o resolver los problemas de concurrencia. Cuando emplea una base de datos independiente para la consulta en partes del sistema y todas las lecturas son descargadas de la base de datos OLTP que actúa el modelo de dominio, pueden ser casi idénticos de lectura confirmada-soluciones basadas en rendimiento y escalabilidad.

Buscar una solución completa

El modelo de modelo de dominio realmente es una herramienta eficaz en manos de un artesano hábil. Como muchos otros desarrolladores, la primera vez que recoge esta herramienta se over-used y puede incluso han abused, con resultados menos estelares. Al diseñar un modelo de dominio, dedicar más tiempo examina los detalles se encuentran en distintos casos de uso en lugar de jumping directamente en las relaciones de entidad de modelado, especialmente tenga cuidado de configurar estas relaciones a fin de mostrar los datos de usuario. Que se atienden mejor con la base de datos simple y sencillo consultar, con posiblemente una capa delgada de facade en la parte superior de algunos independencia del proveedor de base de datos.

Al observar cómo interactúa el código fuera el modelo de dominio con él, busque ágil "más sencilla lo que posiblemente podría trabajar", una llamada de método único en un único objeto del dominio, incluso en el caso cuando se trabaja en varios objetos. Eventos de dominio pueden ayudar redondo fuera de la solución para controlar interacciones más complejas y tecnológicas integraciones, sin introducir cualquier complicaciones.

Al iniciar esta ruta de acceso, tardé algún tiempo adaptarse mi pensamiento, pero las ventajas de cada modelo se pensaban rápidamente. Cuando Empecé a emplear en todos estos modelos juntos, encontré proporcionaron una solución completa para dominios empresariales incluso los más exigentes, manteniendo todo el código en cada parte del sistema pequeño, centrado y probar, todo lo que se desea un desarrollador.

Works citados

Fowler, M. Patterns of Enterprise Application Architecture, (Addison Wesley, 2003).

Udi Dahan Se reconoce como un MVP, un arquitecto principal de IASA y Dr.. Gurú Dobb de SOA, Udi Dahan es el Software Simplist, un consultor independiente, altavoz, autor y instructor proporcionar servicios superiores en la arquitectura orientada a servicios, escalable y segura de empresa y el diseño. Póngase en contacto con Udi a través de su blog en UdiDahan.com.