2009-06-15 28 views
11

Estoy trabajando en mi primer proyecto DDD, y creo que entiendo las funciones básicas de las entidades, los objetos de acceso a los datos y su relación. Tengo una implementación de validación básica que almacena cada regla de validación con su entidad asociada. Esto funciona bien para las reglas que se aplican solo a la entidad actual, pero se desmorona cuando se necesitan otros datos. Por ejemplo, si tengo la restricción de que un nombre de usuario debe ser único, me gustaría que la llamada IsValid() devuelva falso cuando hay un usuario existente con el nombre actual.¿Dónde debo poner un cheque único en DDD?

Sin embargo, no encuentro ninguna forma clara de mantener esta regla de validación en la propia entidad. Me gustaría tener una función IsNameUnique en la entidad, pero la mayoría de las soluciones para hacer esto requerirían que inyecte un objeto de acceso a los datos del usuario. ¿Debería esta lógica estar en un servicio externo? Si es así, ¿cómo guardo la lógica con la entidad misma? ¿O es algo que debería estar fuera de la entidad usuaria?

Gracias!

Respuesta

2

En DDD, hay un concepto llamado aggregate. Es básicamente responsable de la coherencia dentro de la aplicación.

En mi humilde opinión, en este caso específicamente, supongo que el CustomerRepository estaría dentro de algo así como el "agregado del cliente", siendo la clase del cliente la raíz del agregado.

La raíz sería responsable de hacer todo esto, y nadie más podría acceder a las opciones de CustomerRepository. Y hay algunas posibilidades:

  • El CustomerRepository podría lanzar una excepción si el nombre no es único (y el cliente podría capturar y devolver el error, o algo por el estilo)
  • El CustomerRepository podría tener un IsValidUserName (), que el cliente llamaría antes de hacer cualquier otra cosa
  • Cualquier otra opción que se pueda imaginar
3

me gusta la respuesta de Samuel, pero en aras de la simplicidad, yo recomendaría la aplicación de un Specification. Usted crea una Especificación que devuelve un valor booleano para ver si un objeto cumple con ciertos criterios. Inyecte un IUserRepository en la especificación, verifique si un usuario ya existe con ese nombre y devuelva un resultado booleano.

public interface ISpecification<T> 
{ 
    bool IsSatisfiedBy(TEntity entity); 
} 

public class UniqueUsernameSpecification : ISpecification<User> 
{ 
    private readonly IUserRepository _userRepository; 

    public UniqueUsernameSpecification(IUserRepository userRepository) 
    { 
    _userRepository = userRepository; 
    } 

    public bool IsSatisfiedBy(User user) 
    { 
    User foundUser = _userRepository.FindUserByUsername(user.Username); 
    return foundUser == null; 
    } 
} 

//App code  
User newUser; 

// ... registration stuff... 

var userRepository = new UserRepository(); 
var uniqueUserSpec = new UniqueUsernameSpecification(userRepository); 
if (uniqueUserSpec.IsSatisfiedBy(newUser)) 
{ 
    // proceed 
} 
+9

No funciona. Otro hilo puede insertar datos entre la comprobación y el guardado. – dariol

0

Voy a decir que esto está simplemente fuera del alcance de DDD.

Lo que tiene con DDD es una agregación de eventos que producen un modelo útil. Sin embargo, las relaciones de datos entre tales modelos no son necesariamente posibles.

¿Qué modelo de consistencia estás usando?

Si puede confirmar varios eventos en una transacción ACID, puede asegurarse de que los cambios en un grupo de agregados sucedan atómicamente.

Si utiliza un modelo de coherencia eventual, es posible que no pueda validar estas cosas hasta más adelante. Y cuando lo haga, es posible que tenga que compensar las cosas que tienen supuestamente sucedidas pero que ya no son válidas.

La unicidad debe responderse dentro de un contexto. Si su modelo es pequeño (en miles), puede hacer que un agregado represente el conjunto de valores que desea que sean únicos. Suponiendo que una transacción agregada grupal es posible.

Si no, bueno, entonces simplemente proyecta su modelo a una base de datos que soporte una restricción de exclusividad. Si esta proyección falla, debe volver a su agregado y marcarlo de alguna manera como no válido. Todo el tiempo debe considerar el fracaso. Aquí es donde un proceso distribuido de larga duración, como el patrón de la saga, puede ser útil pero también requiere que piense en cosas adicionales.

En resumen, si no puede usar un modelo de almacenamiento con una sólida garantía de consistencia todo se vuelve un lote más complicado. Dicho esto, existen patrones buenos y completos para gestionar fallas en un entorno distribuido, pero el problema se soluciona un poco porque ahora hay que considerar el fracaso, en todo momento, lo cual es bueno, pero requerirá una mayor inversión de tiempo.

Cuestiones relacionadas