Estou tentando entender como e onde implementar fábricas de modelos de domínio. Incluí meu Company
agregado como uma demonstração de como o fiz.
Eu incluí minhas decisões de design no final - eu gostaria de receber comentários, sugestões e críticas sobre esses pontos.
O Company
modelo de domínio:
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;
}
}
A CompanyFactory
fábrica de domínio:
Essa classe é usada para garantir que regras de negócios e invariantes não sejam violados ao criar novas instâncias de modelos de domínio. Ele residiria na camada de domínio.
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);
}
}
O CompanyMapper
mapeador de entidades:
Essa classe é usada para mapear entre modelos de domínio avançado e entidades de dados do Entity Framework. Ele residiria em camadas de infraestrutura.
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
};
}
}
O
Company
construtor é declarado comointernal
.
Razão: Somente a fábrica deve chamar esse construtor.internal
garante que nenhuma outra camada possa instanciar (as camadas são separadas por projetos do VS).O
CompanyFactory.CreateNew(string name)
método seria usado ao criar uma nova empresa no sistema.
Razão: Como ainda não teria sido persistida, uma nova identidade exclusiva precisará ser gerada para ela (usando oIIdentityFactory
).O
CompanyFactory.CreateExisting(int id, string name)
método será usado peloCompanyRepository
ao recuperar itens do banco de dados.
Motivo: o modelo já teria identidade, portanto, isso precisaria ser fornecido à fábrica.O
CompanyMapper.MapFrom(CompanyTable dataEntity)
será usado peloCompanyRepository
ao recuperar dados da persistência.
Razão: Aqui, as entidades de dados do Entity Framework precisam ser mapeadas nos modelos de domínio. OCompanyFactory
será usado para criar o modelo de domínio para garantir que as regras de negócios sejam atendidas.O
CompanyMapper.MapFrom(Company domainEntity)
será usadoCompanyRepository
ao adicionar ou atualizar modelos para persistência.
Motivo: Os modelos de domínio precisam ser mapeados diretamente nas propriedades da entidade de dados, para que o Entity Framework possa reconhecer quais alterações devem ser feitas no banco de dados.
obrigado
CompanyFramework.CreateExisting
construto? Uma coisa que não vejo como fraqueza é aIIdentityFactory
. Se os IDs são gerados a partir de um banco de dados, você deve ter uma mecânica para zombar disso, para permitir testes de unidade com um banco de dados.IIdentityFactory
. No DDD, as entidades precisam ter identidade desde o momento da criação. Infelizmente, não temos o luxo de poder usarGuid
tipos. Temos que gerar aint Ids
partir do banco de dados antes que a persistência ocorra. Outro motivo para uma fábrica.