Pergunta

Eu li algumas das perguntas sobre modelos de domínio anêmicos e separação de interesses. Quais são as melhores técnicas para a realização / colocar lógica de domínio em objetos de domínio anêmicos? No meu trabalho, temos um modelo muito anêmico, e nós estamos usando atualmente classes "ajudante" para executar a lógica de banco de dados / negócios sobre os objetos de domínio. Por exemplo:

public class Customer
{
    public string Name {get;set;}
    public string Address {get;set;}
}

public class Product
{
    public string Name {get;set;}
    public decimal Price {get;set;}
}

public class StoreHelper
{
    public void PurchaseProduct(Customer c, Product p)
    {
         // Lookup Customer and Product in db
         // Create records for purchase
         // etc.
    }
}

Quando as necessidades de aplicativos para fazer uma compra, que criaria o StoreHelper, e chamar o método sobre os objetos de domínio. Para mim, não faria sentido para o cliente / produto para saber como salvar-se a um repositório, mas você provavelmente não gostaria save () métodos nos objetos de domínio. Ele também faria sentido para um método como Customer.Purchase (do produto), mas isso é colocar lógica de domínio sobre a entidade.

Aqui estão algumas técnicas que eu já vêm em frente, não tenho certeza que são bom / mau:

  1. Cliente e herdar produtos de uma classe "Entidade", que fornece as operações básicas CRUD de uma forma genérica (usando um ORM talvez).
    • Pros: Cada objeto de dados, automaticamente obter as operações CRUD, mas são, em seguida, amarrado à / ORM
    • banco de dados
    • Contras: Isso não resolve o problema das operações de negócios nos objetos, e também liga todos os objetos de domínio para uma base Entidade que pode não ser apropriado
  2. Usar classes auxiliares para lidar com as operações CRUD e lógica de negócios
    • Será que faz sentido ter DAOs para as operações de banco de dados "pura", e ajudantes de negócio separadas para as operações específicas de negócios de mais?
    • É melhor usar não-estático ou classes auxiliares estáticos para isso?
    • Prós: objetos de domínio não estão vinculados a qualquer lógica de banco de dados / negócio (completamente anêmico)
    • Contras: não muito OO, não muito natural para usar ajudantes no código do aplicativo (parece código C)
  3. Use a técnica de duplo despacho em que a entidade tem métodos para salvar a um repositório arbitrária
    • Pros: melhor separação de interesses
    • Contras: entidades têm alguma lógica extra anexada (embora seja dissociado)
  4. Em C # 3.0, você pode usar métodos de extensão para anexar os métodos CRUD / negócios para um objeto de domínio sem tocá-lo
    • Esta é uma abordagem válida? Quais são vantagens / desvantagens?
  5. Outras técnicas?

Quais são as melhores técnicas para lidar com isso? Eu sou muito novo para DDD (Estou lendo o livro Evans - talvez isso irá abrir os olhos)

Foi útil?

Solução

Martin Fowler tem escrito muito sobre modelos de domínio, incluindo modelos de domínio anêmicos . Ele também tem descrições breves (e diagramas de classe UML) de muitos padrões de design para modelos de domínio e bancos de dados que podem ser úteis: Catálogo dos " padrões de arquitetura de aplicações corporativas ".

Eu gostaria de sugerir a olhar para o Active Record e padrões Data Mapper . A partir da descrição do seu problema, parece que suas classes auxiliares contêm ambas as regras de domínio / negócios e detalhes da implementação de banco de dados.

O Active Record moveria lógica de domínio do auxiliar e código de banco de dados em outros objetos de domínio (como sua classe base Entity). O Data Mapper moveria lógica de domínio do auxiliar para os objetos de domínio eo código de banco de dados em um objeto de mapa separado. Qualquer abordagem seria mais do que classes auxiliares de estilo processual orientada a objetos.

livro "Domain Driven Design" Eric Evans' é excelente. Ele fica um pouco seca, mas é definitivamente vale a pena. InfoQ tem um "Domain Driven projeto rapidamente" mini-book que é um boa introdução ao livro de Evans. Plus "Domain Driven projeto rapidamente" está disponível como um PDF gratuito.

Outras dicas

Para evitar modelo anêmica, refatorar suas classes auxiliares:

Logic como:
"Customer.PurchaseProduct (produto, pagamento Pagamento)",
"Customer.KillCustomer (assassino Pessoa, arma Arma)"
deve existir para a direita em "Cliente" objeto de domínio.

Logic como:
"Customer.IsCustomerAlive ()"
"Customer.IsCustomerHappy ()"
deve ir com as especificações.

Logic como:
"Customer.Create ()",
"Customer.Update ()"
obviamente, deve ir para repositórios.

Logic como:
"Customer.SerializeInXml ()"
"Customer.GetSerializedCustomerSizeInBytes ()"
deve ir para os serviços.

construtores complexos devem ir para as fábricas.

Isso é como eu vê-lo. Eu ficaria feliz se alguém poderia comentar a minha compreensão da abordagem DDD.


Editar:

Às vezes - modelo de domínio anêmico não deve ser evitada .

Editado minha resposta para acrescentar que DDD não é sobre pegando e soltando padrões.
DDD é sobre nossa maneira de pensar.

Eu sempre pensei do modelo de domínio anêmico como um anti padrão. É claro que um cliente vai comprar produtos, essa capacidade pode ser generised por uma implementação da interface

Interface IPurchase
      Purchase(Product);

, de modo a qualquer um dos seus objetos de domínio pode implementar isso como necessário. Dessa forma você pode introduzir a funcionalidade de seus objetos de domínio - que é exatamente onde deveria estar

.

Uma abordagem que você não mencionou está usando AOP para lidar com seu acesso a dados. Um exemplo de minha recente utilização desta abordagem (embora bastante simplificada para fins de postagem) era que eu tinha uma entidade de domínio Account que tinha um método debit, encapsulando a lógica de negócios necessária para fazer um débito de sucesso da conta.

NB. Todo o código é Java com AspectJ AOP notação ...

public boolean debit(int amount) {
    if (balance - amount >= 0) {
        balance = balance - amount;
        return true;
    }
    return false;
}

Com o repositório adequado injetado em meu aspecto, então eu usei um pointcut às chamadas interceptar a este método ...

pointcut debit(Account account,int amount) :
    execution(boolean Account.debit(int)) &&
    args(amount) &&
    target(account);

... e aplicados alguns conselhos:

after(Account account, int amount) returning (boolean result)  : debit(account,amount) {
    if (result) getAccountRepository().debit(account, amount);
}

Na minha opinião isso dá uma boa separação de interesses, e permite que suas entidades de domínio se concentrar inteiramente na lógica de negócio da aplicação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top