Estoy tratando de entender cómo y dónde implementar fábricas de modelos de dominio. He incluido mi Company
agregado como una demostración de cómo lo hice.
Incluí mis decisiones de diseño al final; agradecería cualquier comentario, sugerencia o crítica sobre esos puntos.
El Company
modelo de dominio:
public class Company : DomainEntity, IAggregateRoot
{
private string name;
public string Name
{
get
{
return name;
}
private set
{
if (String.IsNullOrWhiteSpace(value))
{
throw new ArgumentOutOfRangeException("Company name cannot be an empty value");
}
name = value;
}
}
internal Company(int id, string name)
{
Name = name;
}
}
La CompanyFactory
fábrica de dominios:
Esta clase se utiliza para garantizar que no se infrinjan las reglas comerciales y las invariantes al crear nuevas instancias de modelos de dominio. Residiría en la capa de dominio.
public class CompanyFactory
{
protected IIdentityFactory<int> IdentityFactory { get; set; }
public CompanyFactory(IIdentityFactory<int> identityFactory)
{
IdentityFactory = identityFactory;
}
public Company CreateNew(string name)
{
var id = IdentityFactory.GenerateIdentity();
return new Company(id, name);
}
public Company CreateExisting(int id, string name)
{
return new Company(id, name);
}
}
El CompanyMapper
mapeador de entidades:
Esta clase se utiliza para asignar entre modelos de dominio enriquecido y entidades de datos de Entity Framework. Residiría en capas de infraestructura.
public class CompanyMapper : IEntityMapper<Company, CompanyTable>
{
private CompanyFactory factory;
public CompanyMapper(CompanyFactory companyFactory)
{
factory = companyFactory;
}
public Company MapFrom(CompanyTable dataEntity)
{
return DomainEntityFactory.CreateExisting(dataEntity.Id, dataEntity.Name);
}
public CompanyTable MapFrom(Company domainEntity)
{
return new CompanyTable()
{
Id = domainEntity.Id,
Name = domainEntity.Name
};
}
}
El
Company
constructor se declara comointernal
.
Motivo: solo la fábrica debe llamar a este constructor.internal
asegura que ninguna otra capa pueda instanciarlo (las capas están separadas por proyectos VS).El
CompanyFactory.CreateNew(string name)
método se usaría al crear una nueva compañía en el sistema.
Motivo: dado que aún no se habría persistido, será necesario generar una nueva identidad única para ello (utilizando elIIdentityFactory
).El
CompanyFactory.CreateExisting(int id, string name)
método será utilizado porCompanyRepository
al recuperar elementos de la base de datos.
Motivo: el modelo ya tendría identidad, por lo que debería suministrarse a la fábrica.El
CompanyMapper.MapFrom(CompanyTable dataEntity)
será utilizado porCompanyRepository
al recuperar datos de la persistencia.
Motivo: Aquí, las entidades de datos de Entity Framework deben asignarse a modelos de dominio. SeCompanyFactory
utilizará para crear el modelo de dominio para garantizar que se cumplan las reglas de negocio.El
CompanyMapper.MapFrom(Company domainEntity)
será utilizado porCompanyRepository
al agregar o actualizar modelos a la persistencia.
Motivo: los modelos de dominio deben asignarse directamente a las propiedades de la entidad de datos para que Entity Framework pueda reconocer qué cambios realizar en la base de datos.
Gracias
CompanyFramework.CreateExisting
construcción? Una cosa que no veo como debilidad es elIIdentityFactory
. Si las ID se generan a partir de una base de datos, debe tener una mecánica para burlarse de esto, para permitir pruebas unitarias con una base de datos.IIdentityFactory
. En DDD, las entidades necesitan tener identidad desde el momento de la creación. Desafortunadamente no tenemos el lujo de poder usarGuid
tipos. Tenemos que generarint Ids
desde la base de datos antes de que ocurra la persistencia. Otra razón para una fábrica.