Objetos de negócios - contêineres ou funcionais?


19

Esta é uma pergunta que fiz há algum tempo no SO, mas pode ser discutida melhor aqui ...

Onde trabalho, abordamos esse assunto várias vezes e estamos procurando uma verificação de sanidade. Aqui está a pergunta: os Objetos de Negócios devem ser contêineres de dados (mais parecidos com DTOs ) ou também devem conter lógica que possa executar alguma funcionalidade nesse objeto.

Exemplo - Pegue um objeto de cliente, ele provavelmente contém algumas propriedades comuns (Nome, ID, etc.). Esse objeto de cliente também deve incluir funções (Salvar, Calc, etc.)?

Uma linha de raciocínio diz que separa o objeto da funcionalidade (principal de responsabilidade única) e coloca a funcionalidade em uma camada ou objeto de lógica de negócios.

A outra linha de raciocínio diz que não, se eu tiver um objeto de cliente, quero apenas chamar Customer.Save e terminar com ele. Por que preciso saber sobre outra classe para salvar um cliente se estou consumindo o objeto?

Nossos dois últimos projetos tiveram os objetos separados da funcionalidade, mas o debate foi levantado novamente em um novo projeto.

O que faz mais sentido e por quê?


1
Você poderia nos contar mais sobre o seu projeto? A natureza do seu projeto determinará qual solução é melhor.

Respostas:


5

Se você considera que um Cliente faz parte do modelo de domínio, faz sentido (especialmente no contexto do DDD, mas não se limita a ele) ter propriedades e operações para esse objeto.

Com isso dito, no entanto, acho que o exemplo que você usou é ruim e é a causa do argumento. Se você está falando sobre persistência, os Clientes geralmente não se "salvam"; o que você estiver usando para persistência o fará. Faz sentido que qualquer tipo de persistência deva pertencer à camada / partição de persistência. Geralmente, esse é o pensamento por trás do padrão do repositório. ** Um método como Customer.UpgradeWarranty () ou Customer.PromoteToPreferred () torna o argumento mais claro.

Isso também não remove a possibilidade de ter DTOs . Considere a situação em que você vai passar as informações do cliente para um serviço remoto, por exemplo. Pode não fazer sentido para o cliente criar um DTO para transporte, isso é uma preocupação arquitetônica, mas pode ser feito na persistência ou na camada de rede / partição / código / o que você tem. Nesse caso, esse objeto poderia ter métodos parecidos com este

public static CustomerDTO GetDTO(Customer c) { /* ... */ }

public static Customer GetCustomer(CustomerDTO cdto) { /* ... */ }

Portanto, em resumo, faz todo o sentido ter operações em um objeto de domínio que sejam congruentes com operações lógicas no domínio.

Google para 'Persistence Ignorance' para uma série de boas discussões sobre o assunto ( esta questão SO , e sua resposta aceita é um bom lugar para começar).

** Isso fica um pouco confuso com certos softwares OR / M, nos quais você é forçado a herdar de uma base persistente que possui um método 'Salvar'.


3

Na verdade, recentemente refiz um código para separar os objetos dos dados. O motivo é que os dados são obtidos por meio de um serviço separado, e é muito mais fácil passar os dados brutos para / do servidor, em vez de passar o objeto inteiro para frente e para trás e ter que lidar com erros de validação no servidor.

Para projetos pequenos, fazer com que os objetos contenham sua lógica de negócios e os dados provavelmente estão corretos, mas para projetos grandes ou projetos que podem se expandir com o tempo, eu definitivamente separaria as camadas.


3

Penso que ambas as formas de fazê-lo podem ter seus benefícios, mas eu o consideraria de uma visão orientada a objetos ou orientada a domínio: quais são as responsabilidades das diferentes classes e objetos.

Então, eu me perguntaria isso, no seu caso concreto: um cliente deve saber como se salvar?

Para mim, a resposta seria não: logicamente, para mim, não faz sentido, e um cliente não precisa saber nada sobre qualquer estrutura de persistência (separação de responsabilidades).

Quanto aos exemplos de SnOrfus com Customer.UpgradeWarranty () ou Customer.PromoteToPreferred (), eles são obviamente mais orientados pela lógica de negócios que Customer.Save (). Existem diferentes abordagens para isso, mas, novamente, se você se perguntar se um cliente deve poder atualizar sua garantia, a resposta pode ser sim ou não, dependendo de como você a vê:

  • sim: é claro que um cliente pode atualizar sua garantia
  • não: um cliente pode solicitar a atualização, mas a atualização é feita por outra pessoa

Voltar à sua pergunta original; o caminho que faz mais sentido provavelmente dependerá de quem você perguntar e do método preferido deles, mas o mais importante é provavelmente manter uma maneira de fazê-lo.

Na minha experiência, porém, separar dados e lógica (comercial) contribui para uma arquitetura mais simples, embora não tão empolgante.


2

Eu acho que você está falando especificamente sobre a diferença entre o ActiveRecordpadrão e o Repositorypadrão. No primeiro, as entidades sabem como se manter e, no segundo, o repositório conhece a persistência. Eu acho que o último oferece uma melhor separação de preocupações.

Em um sentido mais amplo, se as entidades agirem mais como uma estrutura de dados, não deverão ter comportamentos, mas se tiverem comportamentos, não deverão ser usadas como uma estrutura de dados. Exemplo:

Estrutura de dados:

class CustomerController
{
    public int MostRecentOrderLines(Customer customer)
    {
        var o = (from order in customer.Orders
                orderby order.OrderDate desc
                select order).First();
        return o.OrderLines.ToList().Count;
    }
}

Estrutura não relacionada a dados:

class Customer
{
    public int MostRecentOrderLines()
    {
        // ... same ...
    }
}

No primeiro caso, você pode navegar pela árvore da estrutura de dados do seu modelo sem problemas, de qualquer lugar do seu código de trabalho, e tudo bem, porque você está tratando como uma estrutura de dados. Neste último caso, o Cliente é mais como um serviço para um determinado cliente, portanto, você não deve chamar métodos no resultado. Todas as coisas que você deseja saber sobre o Cliente devem estar disponíveis chamando um método no objeto Customer.

Então, você deseja que suas entidades sejam estruturas ou serviços de dados? Para consistência, parece melhor ficar com um. No primeiro caso, coloque sua lógica do tipo "serviço" em outro lugar, não na entidade.


1

Eu realmente não posso responder à sua pergunta, mas acho engraçado que também estamos tendo essa discussão em um de nossos projetos na escola. Eu gostaria de separar a lógica dos dados. Mas muitos dos meus colegas de classe dizem que o objeto deve conter toda a lógica E os dados.

Vou tentar aproveitar os fatos que eles trazem à tona:

  • Um objeto de classe de negócios representa uma coisa, portanto todos os dados E lógica devem estar contidos. (por exemplo, se você deseja abrir uma porta, faça você mesmo, não pergunte a outra pessoa. Exemplo ruim, eu sei)

  • Mais fácil de entender o código e a funcionalidade de um objeto bu.

  • Menos complexidade no design

Estou dizendo a eles que eles são preguiçosos e eu faria assim:

  • Sim, as classes de negócios representam coisas e, portanto, mantêm dados. Mas parece errado salvar a si mesmo ou até mesmo copiar. Você não pode fazer isso em rl.

  • Responsabilizar o objeto por tudo não o torna bom para durabilidade e manutenção no futuro. Se você tiver uma classe separada responsável por salvar, se adicionar um novo objeto ao design, poderá implementar facilmente sua função de salvar. em vez de codificar tudo novamente nessa classe.

  • Por ter um objeto capaz de persistir dados, esse objeto pode manter uma conexão com o banco de dados e todo o tráfego do banco de dados é guiado nesse objeto. (basicamente é a camada de acesso a dados), caso contrário, todo o objeto de negócios teria que manter uma conexão. (o que aumenta a complexidade)

Bem, de um jeito ou de outro, vou perguntar a um professor. Quando eu tiver feito isso, postarei a resposta dele aqui também, se você desejar. ;)

editar:

Eu esqueci de mencionar que este projeto era sobre uma livraria.


Seus colegas de classe estão certos, exceto que eles estão confundindo lógica de negócios com outras formas de lógica (neste caso, lógica de arquitetura ou persistência).
Steven Evers

1

A resposta realmente vai depender de qual é a sua arquitetura / design - uma arquitetura DDD com um modelo de domínio será muito diferente de um modelo centrado em dados CRUD quando se trata de design de objetos.

Em geral, porém, lembre-se de que no design orientado a objetos você está tentando encapsular o estado e expor o comportamento. Isso significa que você geralmente tentará obter o estado associado a um comportamento o mais próximo possível desse comportamento - quando você não fizer isso, será forçado a expor o estado de uma forma ou de outra.

Em um modelo de domínio, você deseja separar o modelo de negócios das preocupações com a infraestrutura - para evitar absolutamente métodos como '.Save'. Não me lembro da última vez que "salvei" um cliente na vida real!

Em um aplicativo CRUD, os dados são os cidadãos de primeira classe, portanto, um ".Save" é totalmente apropriado.

A maioria das aplicações consideráveis ​​do mundo real terá uma mistura desses paradigmas - modelo de domínio em que existem regras de negócios complexas ou em rápida mudança, DTOs para transferência de dados através de fronteiras, CRUD (ou algo entre CRUD e um modelo de domínio, como o registro de ativos) em outros lugares. Não existe uma regra única para todos.


0

Estive refletindo sobre a mesma pergunta há algum tempo - acho que o componente de dados e o componente lógico devem estar separados. Acredito nisso porque leva você ao estado de espírito certo em relação à lógica de negócios como uma interface para os dados que fornecem significado.

Eu também modificaria o ponto de Scott Whitlock a partir de cima (exceto que eu não tenho nenhum ponto em ser um novo membro), as classes de dados ou de lógica de negócios realmente não precisam se preocupar com a maneira como o objeto é armazenado persistentemente.

Dito isto, se você estiver lidando com um produto existente, desde que tenha interfaces contratuais limpas - tudo bem e com manutenção também ...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.