Pergunta

Eu estava olhando para as diferenças href="https://stackoverflow.com/questions/725348/poco-vs-dto"> (Parece que de POCO são DTO com comportamento (métodos?)) e deparei este artigo por Martin Fowler sobre o modelo de domínio anêmico .

Através de falta de compreensão, eu acho que eu criei um destes modelos anêmica domínio.

Em uma das minhas aplicações eu tenho minhas entidades de domínio de negócios definidos em um 'dto' dll. Eles têm um monte de propriedades com getter de e setter do e não muito mais. Meu código lógica de negócios (populate, calcule) está em outro 'bll' dll, e meu código de acesso de dados está em um 'dal' dll. 'Melhores práticas', pensei.

Assim, normalmente eu criar um dto assim:

dto.BusinessObject bo = new dto.BusinessObject(...)

e passá-lo para a camada de bll assim:

bll.BusinessObject.Populate(bo);

que, por sua vez, executa alguma lógica e passa para a camada dal assim:

dal.BusinessObject.Populate(bo);

No meu entendimento, para fazer o meu dto é a necessidade I de POCO para fazer a parte lógica de negócios e comportamento (métodos) do objeto. Então, ao invés do código acima é mais como:

poco.BusinessObject bo = new poco.BusinessObject(...)
bo.Populate();

ie. Eu estou chamando o método sobre o objeto em vez de passar o objeto para o método.

A minha pergunta é - como posso fazer isso e ainda manter a 'melhor prática' de camadas de preocupações (de dll separado etc ...). não chamar o método no objeto significa que o método deve ser definido no objeto?

Por favor, ajude a minha confusão.

Foi útil?

Solução

Normalmente, você não quer introduzir persistência em seus objetos de domínio, uma vez que não faz parte desse modelo de negócio (um avião não construir-se, ele voa de passageiros / carga de um local para outro). Você deve usar a repositório padrão, um quadro ORM , ou algum outro padrão de acesso de dados para gerenciar o armazenamento persistente e retreival de estado de um objeto .

Quando o modelo de domínio anêmico vem para jogar é quando você está fazendo as coisas como esta:

IAirplaneService service = ...;
Airplane plane = ...;
service.FlyAirplaneToAirport(plane, "IAD");

Neste caso, a gestão do estado do avião (se ele está voando, onde está, qual é a hora de partida / aeroporto, o que é a hora de chegada / aeroporto, o que é o plano de vôo, etc) é delegada a algo externo ao avião ... a instância AirplaneService.

A POCO forma de implementar isso seria para projetar sua interface desta maneira:

Airplane plane = ...;
plane.FlyToAirport("IAD");

Isto é mais visível, uma vez que desenvolvedores saibam onde procurar para fazer um avião voar (basta dizer o avião para fazê-lo). Ele também permite que você para garantir que o estado é única gerido internamente. Você pode então fazer coisas como localização actual só de leitura, e garantir que ele só mudou em um só lugar. Com um objeto de domínio anêmico, já que estado está definido externamente, descobrindo onde o estado é alterado torna-se cada vez mais difícil como a escala do seu domínio aumenta.

Outras dicas

Eu acho que a melhor maneira de esclarecer esta é, por definição:

DTO: Transferência de dados objetos:

Eles só servem para o transporte de dados tipicamente entre a camada de apresentação e camada de serviço. Nada menos ou mais. Geralmente, ele é implementado como classe com Obtém e define.

public class ClientDTO
{
    public long Id {get;set;}
    public string Name {get;set;}
}

BO: Business Objects:

Os objetos de negócios representa os elementos de negócios e, naturalmente, a melhor prática diz que eles devem conter lógica de negócios também. Como foi dito por Michael Meadows, também é uma boa prática para acesso a dados isolado a partir desta objetos.

public class Client
{
    private long _id;
    public long Id 
    { 
        get { return _id; }
        protected set { _id = value; } 
    }
    protected Client() { }
    public Client(string name)
    {
        this.Name = name;    
    }
    private string _name;
    public string Name
    {
        get { return _name; }
        set 
        {   // Notice that there is business logic inside (name existence checking)
            // Persistence is isolated through the IClientDAO interface and a factory
            IClientDAO clientDAO = DAOFactory.Instance.Get<IClientDAO>();
            if (clientDAO.ExistsClientByName(value))
            {
                throw new ApplicationException("Another client with same name exists.");
            }
            _name = value;
        }
    }
    public void CheckIfCanBeRemoved()
    {
        // Check if there are sales associated to client
        if ( DAOFactory.Instance.GetDAO<ISaleDAO>().ExistsSalesFor(this) )
        {
            string msg = "Client can not be removed, there are sales associated to him/her.";
            throw new ApplicationException(msg);
        }
    }
}

Serviço ou Classe Aplicação Essas classes representam a interação entre usuário e o sistema e eles vão fazer uso de ambos ClientDTO e cliente.

public class ClientRegistration
{
    public void Insert(ClientDTO dto)
    {
        Client client = new Client(dto.Id,dto.Name); /// <-- Business logic inside the constructor
        DAOFactory.Instance.Save(client);        
    }
    public void Modify(ClientDTO dto)
    {
        Client client = DAOFactory.Instance.Get<Client>(dto.Id);
        client.Name = dto.Name;  // <--- Business logic inside the Name property
        DAOFactory.Instance.Save(client);
    }
    public void Remove(ClientDTO dto)
    {
        Client client = DAOFactory.Instance.Get<Client>(dto.Id);
        client.CheckIfCanBeRemoved() // <--- Business logic here
        DAOFactory.Instance.Remove(client);
    }
    public ClientDTO Retrieve(string name)
    {
        Client client = DAOFactory.Instance.Get<IClientDAO>().FindByName(name);
        if (client == null) { throw new ApplicationException("Client not found."); }
        ClientDTO dto = new ClientDTO()
        {
            Id = client.Id,
            Name = client.Name
        }
    }
}

Pessoalmente eu não encontrar esses modelos Anemic domínio tão ruim; Eu realmente gosto da idéia de ter objetos de domínio que representam apenas dados, não o comportamento. Eu acho que a principal desvantagem dessa abordagem é descoberta do código; você precisa saber quais ações que estão disponíveis para usá-los. Uma maneira de contornar isso e ainda manter o código de comportamento dissociado do modelo é a introdução de interfaces para o comportamento:

interface ISomeDomainObjectBehaviour
{
    SomeDomainObject Get(int Id);
    void Save(SomeDomainObject data);
    void Delete(int Id);
}

class SomeDomainObjectSqlBehaviour : ISomeDomainObjectBehaviour
{
    SomeDomainObject ISomeDomainObjectBehaviour.Get(int Id)
    {
        // code to get object from database
    }

    void ISomeDomainObjectBehaviour.Save(SomeDomainObject data)
    {
        // code to store object in database
    }

    void ISomeDomainObjectBehaviour.Delete(int Id)
    {
        // code to remove object from database
    }
}
class SomeDomainObject
{
    private ISomeDomainObjectBehaviour _behaviour = null;
    public SomeDomainObject(ISomeDomainObjectBehaviour behaviour)
    {

    }

    public int Id { get; set; }
    public string Name { get; set; }
    public int Size { get; set; }


    public void Save()
    {
        if (_behaviour != null)
        {
            _behaviour.Save(this);
        }
    }

    // add methods for getting, deleting, ...

}

Dessa forma, você pode manter a implementação comportamento separado do modelo. O uso de implementações de interface que são injetadas no modelo também torna o código bastante fácil de teste, pois você pode facilmente zombar do comportamento.

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