ti-enxame.com

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

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

Onde eu trabalho, já falamos sobre esse assunto várias vezes e estamos procurando uma verificação de sanidade. Aqui está a pergunta: Os Business Objects devem ser contêineres de dados (mais como DTOs ) ou eles 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 separe o objeto da funcionalidade (principal de responsabilidade única) e coloque a funcionalidade em uma camada ou objeto de lógica de negócios.

A outra linha de raciocínio diz: não, se eu tiver um objeto de cliente, quero apenas ligar para Cliente. Salve e pronto. 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 sobre um novo projeto.

O que faz mais sentido e por quê ??

19
Walter

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

Com isso dito, entretanto, acho que o exemplo que você usou é pobre e é a causa da discussão. Se você está falando sobre persistência, os clientes geralmente não se "salvam"; tudo o que você está usando para persistência, sim. Faz sentido que qualquer tipo de persistência deva pertencer à camada/partição de persistência. Este é geralmente o pensamento por trás do padrão de 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 informações do cliente para um serviço remoto, por exemplo. Pode não fazer sentido para um cliente criar um DTO para transporte, essa é uma preocupação arquitetônica, mas um caso poderia ser feito para isso na persistência ou camada de rede/partição/código/o que você quiser. Nesse caso, tal objeto poderia ter métodos semelhantes a 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 as operações lógicas no domínio.

Procure no Google por 'Persistência Ignorância' para uma série de boas discussões sobre o assunto ( esta SO questão , e sua resposta aceita é um bom lugar para começar).

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

5
Steven Evers

Eu acho que as duas maneiras de fazer isso podem ter seus benefícios, mas eu olharia para isso de uma visão orientada a objetos ou controlada por domínio: quais são as responsabilidades das diferentes classes e objetos.

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

Para mim, a resposta seria não: logicamente para mim isso não faz sentido, e um cliente não deveria ter que saber nada sobre nenhum framework de persistência (separação de responsabilidades).

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

  • sim: um cliente pode, é claro, atualizar sua garantia
  • não: um cliente pode pedir fazer o upgrade, mas o upgrade é feito por outra pessoa

De volta à sua pergunta original; qual forma faz mais sentido provavelmente dependerá de a quem você pergunta e do método preferido delas, mas o mais importante provavelmente é manter uma maneira de fazê-lo.

Porém, em minha experiência, separar dados e lógica (de negócios) torna a arquitetura mais simples, embora não tão empolgante.

3
Vetle

Na verdade, apenas recentemente retrabalhei algum 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 apenas passar os dados brutos de/para o servidor em vez de passar o objeto inteiro de um lado para o outro e ter que lidar com erros de validação no servidor.

Para projetos pequenos, ter objetos contendo sua lógica de negócios e dados provavelmente é bom, mas para projetos grandes ou projetos que podem se expandir com o tempo, eu definitivamente separaria as camadas.

3
Rachel

Acho que você está falando especificamente sobre a diferença entre o padrão ActiveRecord e o padrão Repository. No primeiro, as entidades sabem como persistir, e no segundo, o repositório sabe sobre persistência. Acho que o último oferece uma melhor separação de preocupações.

Em um sentido mais amplo, se as entidades agem mais como uma estrutura de dados, então elas não deveriam ter comportamentos, mas se tivessem comportamentos, então não deveriam 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 sem dados:

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

No primeiro caso, você pode navegar pela árvore de estrutura de dados do seu modelo sem problemas, de qualquer lugar em seu código de trabalho, e tudo bem, porque você está tratando isso como uma estrutura de dados. No último caso, o Cliente é mais como um serviço para um determinado cliente, então 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 Cliente.

Então, você quer que suas entidades sejam estruturas de dados ou serviços? 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.

2
Scott Whitlock

A resposta vai realmente 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 objeto.

Em geral, entretanto, tenha em mente 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 daquele comportamento - quando você não consegue fazer isso, é 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 questões de infraestrutura - portanto, você absolutamente deseja evitar 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 dos aplicativos consideráveis ​​do mundo real terá uma mistura desses paradigmas - modelo de domínio onde há mudanças rápidas ou regras de negócios complexas, DTOs para transferência de dados entre fronteiras, CRUD (ou algo entre CRUD e um modelo de domínio, como o registro de ativação) em outro lugar. Não existe uma regra única para todos.

1
FinnNk

Não posso responder sua pergunta, mas acho engraçado também estarmos tendo essa discussão em um de nossos projetos na escola. Eu mesmo gostaria de separar a lógica dos dados. Mas muitos dos meus colegas dizem que o objeto deve conter todos os dados lógicos AND.

Vou tentar resumir os fatos que eles trazem:

  • Um objeto de classe de negócios representa uma coisa, então todos os dados E lógica devem estar contidos. (por exemplo, se você quiser abrir uma porta, faça você mesmo, não pergunte a ninguém. Mau exemplo, eu sei)

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

  • Menos complexidade no design

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

  • Sim, classes de negócios representam coisas, portanto, armazenam dados. Mas parece errado salvar a si mesmo, ou mesmo copiar. Você não pode fazer isso na rl.

  • Tornar o objeto responsável 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 de novo nessa classe.

  • Por ter um objeto capaz de persistir dados, esse objeto pode manter uma conexão de banco de dados e todo o tráfego do banco de dados é direcionado para esse 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 adiciona complexidade)

Bem, de uma forma ou de outra, vou perguntar a um professor. Quando eu tiver feito isso, postarei sua resposta aqui também, se você quiser. ;)

editar:

Esqueci de mencionar que esse projeto era sobre uma livraria.

1
Emerion

Estou refletindo sobre a mesma questão há um tempo - acho que o componente de dados e o componente lógico devem ser separados. Acredito nisso porque isso coloca você no estado de espírito certo em relação à lógica de negócios como uma interface para os dados que fornece significado.

Eu também modificaria o ponto de Scott Whitlock de cima (exceto que não tenho pontos em ser um novo membro), dados ou classes de lógica de negócios não deveriam realmente se preocupar sobre como o objeto é armazenado persistentemente.

Dito isso, se você estiver lidando com um produto existente, contanto que tenha interfaces contratuais limpas - isso está OK e pode ser mantido também ...

0
heretik