Pergunta

Eu tenho um modelo de domínio rico, onde a maioria das classes têm algum comportamento e algumas propriedades que são ou calculados ou expor as propriedades de objetos de membros (o que quer dizer que os valores dessas propriedades nunca são persistentes).

Meu cliente fala para o servidor apenas através WCF.

Como tal, para cada entidade de domínio, que tem um DTO correspondente - uma representação simples que contém apenas os dados - bem como uma classe mapeador que implementa DtoMapper<DTO,Entity> e pode converter uma entidade para a sua DTO equivalente ou vice-versa através um gateway estática:

var employee = Map<Employee>.from_dto<EmployeeDto>();

O lado do servidor desta aplicação é principalmente sobre a persistência, onde meus DTOs vir do serviço WCF, são desserializado, em seguida, uma arbitrariedade persiste ORM-los para o banco de dados ou um pedido de consulta vem de WCF e os executa ORM que consulta no banco de dados e retorna objetos a ser serializado e enviado de volta pelo WCF.

Diante desse cenário, isso faz qualquer sentido para mapear minha loja persistência para as entidades de domínio, ou devo apenas mapear diretamente para os DTOs?

Se eu usar entidades de domínio, o fluxo seria

  1. solicitações do cliente objeto
  2. transmite WCF solicitar ao servidor
  3. ORM consulta entidades de domínio de banco de dados e retorna
  4. entidades de domínio transformado em DTOs pelo mapeador
  5. WCF serializa DTO e retorna ao cliente
  6. cliente desserializa DTO
  7. DTO transformada em entidade de domínio por mapeador
  8. viewmodels criado, etc.

semelhante na viagem de retorno

Se eu mapear diretamente para DTO, posso eliminar um mapeamento por objecto, por solicitação. O que eu perder, fazendo isso?

A única coisa que vem à mente é mais uma oportunidade para validar antes de insert / update, porque eu não tenho nenhuma garantia de que o DTO foi sempre sujeita a validação ou sequer existia como uma entidade de domínio antes de ser enviado através do fio, e eu acho uma chance de validar na seleção (se outro processo poderia ter colocado valores inválidos no banco de dados). Existem outras razões? São estas razões suficientes para justificar as etapas de mapeamento adicionais?

edit:

Eu disse "ORM arbitrária" acima, e eu quero que as coisas sejam como ORM-and-persistence-agnóstico quanto possível, mas se você tiver nada de especial a acrescentar que é específico para NHibernate, por todos os meios fazer.

Foi útil?

Solução

Eu pessoalmente recomendo manter o seu mapeamento no lado do servidor. Você provavelmente já fez um monte de trabalho a construção de seu projeto para o ponto é no agora; Não jogue fora.

Considere o que um serviço web é. Não é meramente uma abstração sobre o seu ORM; é um contrato . É uma API pública para os seus clientes, tanto internos como externos.

A API pública deve ter pouca ou nenhuma razão para mudar. Quase qualquer mudança para uma API, além de adicionar novos tipos e métodos, é uma mudança de ruptura. Mas seu modelo de domínio não vai ser tão rigoroso. Você vai precisar para mudar isso de vez em quando como você adicionar novos recursos ou descobrir falhas no projeto original. Você quer ser capaz de assegurar que as alterações ao seu modelo interno não causam cascata mudanças através de contrato do serviço.

Na verdade, é uma prática comum (eu não vou leitores insulto com a frase "melhor prática") para criar classes específicas Request e Response para cada mensagem por um motivo semelhante; torna-se muito mais simples para estender a capacidade dos serviços e métodos existentes sem que elas sejam alterações significativas.

Os clientes provavelmente não deseja exatamente o mesmo modelo que você usa internamente no serviço. Se você é seu único cliente, então talvez isso parece transparente, mas se você tiver clientes externos e ter visto o quão longe a sua interpretação do seu sistema muitas vezes pode ser, então você vai entender o valor de não permitir que o seu modelo perfeito para vazamento fora dos limites da API de serviço.


E, às vezes, não é mesmo possível para enviar o modelo de volta através da API. Existem muitas razões pelas quais isso pode ocorrer:

  • Os ciclos no gráfico de objeto. Perfeitamente bem em OOP; desastrosa na serialização. Você acaba tendo que fazer escolhas permanentes dolorosas sobre o qual "direção" do gráfico deve ser serializada na. Por outro lado, se você usar um DTO, você pode serializar em qualquer direção que você quer, o que ternos a tarefa em mãos.

  • A tentativa de usar certos tipos de mecanismos de herança sobre o SOAP / REST pode ser um truque na melhor das hipóteses. O serializador XML de estilo antigo, pelo menos apoios xs:choice; DataContract não, e eu não vou tergiversar sobre lógica, mas basta dizer que você provavelmente tem algum polimorfismo no seu modelo de domínio rico e é quase impossível para canalizar que através do serviço web.

  • preguiçoso / carregamento diferido, que você provavelmente fazer uso de se você usar um ORM. É o suficiente estranho ter certeza que ele fica serializado corretamente - por exemplo, usando o Linq para entidades SQL, WCF nem sequer acionar o carregador de preguiçoso, ele vai apenas colocar null nesse campo a menos que você carregá-lo manualmente - mas o problema fica ainda pior para dados provenientes de volta em algo tão simples como um List<T> auto-propriedade que é inicializado no construtor -. bastante comum em um modelo de domínio - simplesmente não funciona no WCF, porque não invoca o seu construtor. Em vez disso você tem que adicionar um [OnDeserializing] initializer método, e você realmente não quer sobrecarregar o seu modelo de domínio com este lixo.

  • Eu também só notou a observação entre parênteses que você usa NHibernate. Considere-se que interfaces como IList<T> não pode ser serializado em todo um serviço de web! Se você usar classes POCO com NHibernate, como a maioria de nós fazer, então isso simplesmente não vai funcionar, período.


Há também será provavelmente muitos casos em que o seu modelo de domínio interno simplesmente não corresponder às necessidades do cliente, e não faz sentido mudar seu modelo de domínio para acomodar essas necessidades. Como exemplo disso, vamos dar algo tão simples como uma factura. Ele precisa mostrar:

  • Informações sobre a conta (número de conta, nome, etc.)
  • dados específicos-Invoice (número da fatura, data, data de vencimento, etc.)
  • Informações /-level R A (saldo anterior, encargos moratórios, novo equilíbrio)
  • produtos ou informações de serviço para tudo na factura;
  • Etc.

Isso provavelmente se encaixa muito bem dentro de um modelo de domínio. Mas e se o cliente deseja executar um relatório que mostra 1.200 dessas faturas? Algum tipo de relatório de reconciliação?

Isso é péssimo para a serialização. Agora que você está enviando 1.200 faturas com os mesmos dados sendo serializada uma e outra vez - mesmas contas, os mesmos produtos, mesmo de F / R. Internamente, a sua aplicação é manter o controle de todas as ligações; ele sabe a fatura nº 35 e Nota Fiscal nº 45 são para o mesmo cliente e, portanto, compartilham uma referência Customer; toda esta informação é perdida após a serialização e você acaba enviando uma quantidade ridícula de dados redundantes.

O que você realmente quer é enviar um relatório personalizado que inclui:

  • Todas as contas incluídas no relatório, e seu A / R;
  • Todos os produtos incluídos no relatório;
  • Todas as facturas, com o Produto e Conta IDs somente.

Você precisa executar "normalização" adicional em seus dados de saída antes de enviá-lo para o cliente, se você quiser evitar a redundância maciça. Este fortemente favorece a abordagem DTO; não faz sentido ter esta estrutura no seu modelo de domínio porque o seu modelo de domínio cuida de despedimentos, em sua própria maneira.

Espero que aqueles são suficientes exemplos e racional o suficiente para convencê-lo a manter seus mapeamentos de domínio <-> Contrato de Serviço intacta. Você já fez absolutamente a coisa certa até agora, você tem um grande projeto, e seria uma vergonha para negar todo o esforço em favor de algo que poderia levar a grandes dores de cabeça mais tarde.

Outras dicas

Você vai precisar para mapear os DTOs no lado do cliente de qualquer maneira, por isso, por simetria, é melhor fazer o mapeamento inverso no lado do servidor. Desta forma, você isolar as suas conversões em camadas de abstração bem separados.

camadas de abstração são boas não só para validações, mas para isolar o código de mudanças abaixo / acima dele, e fazer o seu código mais testável e com menos repetições.

Além disso, a menos que você notar uma grande gargalo de desempenho na conversão extra, lembre-se: a otimização precoce é a raiz de todo mal. :)

Você deve definitivamente manter suas entidades de domínio separado do DTO é que eles são diferentes preocupações. do DTO são geralmente heriachal, modelos de auto-descrevendo onde como suas entidades de domínio sobre o outro encapsular lado sua lógica de negócio e tem um monte de comportamento anexado com eles.

Tendo dito que eu não tenho certeza onde o mapeamento extra é? Você recuperar os dados usando o seu ORM (aka entidades de domínio) e mapear esses objetos ao seu DTO para que haja apenas 1 mapeamento lá? BTW, se você não estiver usar algo como AutoMapper para fazer o mapeamento tedioso para você.

Estas mesmas do DTO são então desserializado para o cliente ea partir daí você pode mapear diretamente para os seus UIViewModels. Então, a grande figura é algo como:

  • cliente solicita entidade por Id do serviço WCF
  • WCF Serviço recebe entidade a partir Repository / ORM
  • Usa um AutoMapper para mapear de entidade para DTO
  • Cliente recebe DTO
  • Usa um AutoMapper para mapear para o UI ViewModel
  • UIViewModel é binded à GUI

Quando você diz que seu aplicativo do lado do servidor é "principalmente" sobre persistência, eu acho que essa é a principal coisa a se pensar. Existe realmente um modelo de domínio do lado do servidor que requer alguma inteligência em torno dos dados que ele recebe ou faz o seu serviço WCF puramente agir como o gateway entre seu modelo de domínio e o armazenamento de dados?

Além disso, considere se o seu DTO é projetado para o domínio do cliente.
É este o único domínio do cliente que precisa de acesso a esse armazenamento de dados através de seu serviço?
São os DTOs do lado do servidor ou flexível o suficiente para servir um domínio de aplicativo diferente de grão grosso?
Se não, então é provavelmente vale a pena o esforço para manter as implementações de interface externa abstraídas.

(DB> ORM-> EmployeeEntity-> Client1DTOAssembler-> Client1EmployeeDTO).

Nós temos uma aplicação semelhante, onde um serviço WCF actua principalmente como um gateway para o armazenamento de dados persistente.

No nosso caso, nosso cliente e servidor não use o conjunto contendo "DTOs." Isso nos dá a oportunidade de simplesmente adicionar código para as classes parciais gerados pelo serviço de referência, de modo que muitas vezes são capazes de usar um DTO como está no lado do cliente e tratá-lo como um objeto de domínio. Outras vezes podemos ter do lado do cliente somente objetos de domínio que servem como fachadas para um monte de objetos persistentes que chegamos a partir do serviço WCF.

Quando você pensa sobre os comportamentos e propriedades computadas que seus objetos de domínio têm, quanto sobreposição está lá, realmente entre o cliente e servidor? No nosso caso, nós determinamos que a divisão de responsabilidades entre cliente e servidor significava que havia muito pouco, se houver, código que precisava estar presente (e exatamente o mesmo) no cliente e servidor.

Para responder às suas perguntas diretamente, se o seu objetivo é permanecer completamente persistência agnóstico, eu certamente mapear o armazenamento de persistência a seus objetos de domínio e, em seguida, mapear para DTOs. Há muitas implementações de persistência que podem sangrar em seus objetos e complicar usá-los como WCF DTOs.

No lado do cliente, pode não ser necessário fazer um mapeamento adicional se você pode simplesmente decorar ou aumentar suas DTOs e que é uma solução muito simples.

A sua arquitetura parece muito bem pensado. Meu gut-sentido é, se você já decidiu reduzir os objetos para DTO de enviá-los por meio do WCF, e você atualmente não têm uma necessidade para a funcionalidade do objeto adicional sobre o lado do servidor, por que não manter as coisas simples e mapa o armazenamento de persistência diretamente aos DTOs.

O que você perdeu? Eu não acho que alguma coisa realmente perder. Você é a arquitetura é limpo e simples. Se você decidir no futuro que há uma nova necessidade de uma funcionalidade mais rica no lado do servidor, você sempre pode re-fator nesse ponto para recriar as entidades de domínio lá.

Eu gosto de mantê-lo simples e re-fator como necessário mais tarde, para tentar evitar a coisa otimização pré-amadurecer, etc.

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