Este artigo foi traduzido por máquina.

Framework de entidade

Anti-Patterns para evitar em aplicativos da camada N

Daniel Simmons

Este artigo discute:

  • Noções básicas sobre fileiras
  • Não distribua seus objetos!
  • Serviço personalizado ou serviço RESTful?
  • Alguns antipadrões de n camadas
Este artigo usa as seguintes tecnologias:
Framework de entidade

Conteúdo

Noções básicas sobre N camadas
Anti-Pattern 1: acoplamento justo
Anti-Pattern 2: requisitos de estáticos supondo que
Anti-Pattern 3: concorrência Mishandled
Anti-Pattern 4: serviços com monitoração de estado
Fazendo-se anti-Pattern nº 5: duas camadas passar três
Anti-Pattern nº 6: Undervaluing simplicidade

como um membro da entidade Fale Framework equipe, freqüentemente com os clientes sobre a criação de aplicativos que usam a estrutura de entidades. Provavelmente o tópico obter perguntei sobre maior que a coisa é criar aplicativos de várias camadas. Neste artigo, tentarei definir uma base no qual você pode criar para o sucesso nesta parte de seus aplicativos. A maioria do artigo é dedicada para criar os antipadrões para várias camadas, que geralmente são os problemas mais importantes que localizar. Este é um tópico em que há muita de opções e muitos problemas a serem consideradas, portanto, ele é importante compreender o espaço total antes de tomar decisões para seu aplicativo em particular. Nos artigos futuros, VOU examinar padrões de n camadas para êxito e alguns dos principais APIs e questões específicas a estrutura de entidades e fornecer um pico dê recursos presente na Microsoft .NET Framework 4 que deve fazer fileiras significativamente mais fácil.

Noções básicas sobre N camadas

Antes de eu mergulhar nos antipadrões, é importante ter um entendimento comum de várias camadas.

O primeiro ponto a ser claro em é a diferença entre fileiras e camadas. Um aplicativo bem projetado terá várias camadas com dependências cuidadosamente gerenciadas. Essas camadas poderiam moram em uma única camada ou ser divididas em várias camadas. Uma camada é apenas um conceito organizacional em um aplicativo, enquanto uma camada denota separação física ou pelo menos um design permitirá que a separação física se necessário.

Qualquer aplicativo que ele falou um banco de dados tem mais de uma camada a menos que esse banco de dados é executado em processo, mas o aplicativo não é chamado com várias camadas, a menos que ela envolve a camadas mais que apenas o banco de dados e o aplicativo. Da mesma forma, cada aplicativo ASP.NET que envolve um banco de dados é tecnicamente fileiras pois há em banco de dados, o servidor Web e o navegador. A menos que você introduzir serviços WCF (Windows Communication Foundation) ou da Web, você não chamaria esse aplicativo fileiras como para a maioria dos fins da servidor e navegador podem ser pensados como uma camada de único cliente. Aplicativos de N camadas são aqueles que têm no mínimo uma camada de banco de dados, uma camada intermediária que expõe um serviço e uma camada de cliente.

Enquanto ele não som como grande um muito na superfície, acontece que a implementação de dividir em várias camadas de aplicativos é difícil. Há muito mais armadilhas que você imagina. Esses armadilhas levaram Fowler Martin, no seu catálogo de padrões de aplicativo arquitetura (Addison-Wesley, 2002), para criar uma instrução muito forte no assunto:

Não distribua seus objetos!

Martin chama o primeiro lei do Distributed Objects.

Como com cada regra de design, no entanto, há ocasiões quando a lei deve ser definida reserve. Seu aplicativo pode ter um problema de escalabilidade que requer várias camadas, portanto, você pode aplicar mais recursos de computação. Talvez você precisa trocar dados com um aplicativo do parceiro ou cliente de negócios. Apenas pode ser que você tenha segurança ou restrições de infra-estrutura que dividem seu aplicativo para vários computadores ou impedir que uma parte da seu aplicativo de falando diretamente outra parte. Quando você precisar várias camadas, você realmente precisa-los.

Enquanto os antipadrões apresentados neste artigo podem ser aplicados a uma grande variedade de aplicativos e tecnologias, o foco principal será criar e consumir serviços WCF personalizados que persistem dados usando a estrutura de entidades.

Não é surpresa, várias fileiras antipadrões são resultado de perda de enfatizar o objetivo do seu aplicativo. Se você não mantenha em mente que motivou o uso de uma arquitetura de várias camadas em primeiro lugar, ou se você esquecer de questões críticas persistência, em seguida, é todos os muito fácil obter em problemas. As seções a seguir veremos alguns problemas comuns.

Serviço personalizado ou serviço RESTful?

REST, ou representações estado transferência, é um tipo de serviço da Web que está rapidamente ganhando popularidade. Portanto, você pode pergunte-se o que é a diferença entre serviços RESTful e serviços da Web personalizados e por que você pode escolher um tipo a outro. A diferença chave entre os dois tipos é que os serviços REST são centralizada de recursos enquanto serviços personalizados estão centrados em operação. Com o REST, você divide os dados em recursos, fornecer uma URL de cada recurso e implementar as operações padrão sobre os recursos que permitir a criação, recuperação, atualização e exclusão (CRUD). Com os serviços personalizados, você pode implementar qualquer método arbitrário, que significa que o foco é as operações em vez dos recursos e as operações pode ser adaptada às necessidades específicas de seu aplicativo.

Alguns serviços se ajustam muito naturalmente o modelo REST — geralmente quando os recursos são óbvios e grande parte do serviço envolve o gerenciamento desses recursos. O Exchange Server, por exemplo, tem uma API de REST para organizar itens de email e calendário. Da mesma forma, há são compartilhamento de fotos da Web sites na Internet que expõem APIs de REST. Em outros casos, os serviços menos claramente coincidir com operações de REST, mas ainda podem ser feitos para ajustar. Enviar email, por exemplo, pode ser feito adicionando um recurso a uma pasta de caixa saída. Essa não é a maneira que mais naturalmente pensa enviar email, mas não é muito de um Alongar.

Em outros casos, entretanto, operações de REST apenas não se ajustam bem. Criar um recurso para iniciar um fluxo de trabalho que conduz mensal da folha de pagamento seleção impressão, por exemplo, poderia ser muito menos natural do que ter um método específico para esse fim.

Se você pode ajustar o serviço para as restrições de REST, fazer então irá adquirir você muita vantagens. Serviços de dados ADO.NET em combinação com a estrutura de entidades torna mais fácil criar serviços RESTful e clientes para trabalhar com eles. A estrutura pode fornecer que mais funcionalidade a RESTful serviços automaticamente porque os serviços são restritas a seguir um padrão específico. Além disso, serviços RESTful ter um alcance muito amplo porque elas são tão simples e interoperável. Eles funcionam especialmente bem quando você não souber antecipadamente que os clientes podem ser. Finalmente, REST pode ser feita para dimensionar a lidar com muito grandes volumes de operações.

Para muitos aplicativos, as restrições de REST são apenas muito grande. Às vezes, o domínio não divide claramente em um único padrão de recursos ou as operações envolvem vários recursos ao mesmo tempo. Às vezes, as ações de usuário e lógica comercial em torno delas não mapeiam bem para operações RESTful, ou um controle mais preciso é necessário não couber dessas operações. Nesses casos, os serviços personalizados são a maneira para ir.

Você sempre pode criar um aplicativo que tem uma mistura de REST e serviços personalizados. Com freqüência a solução ideal para um aplicativo é uma combinação de ambos.

Anti-Pattern 1: acoplamento justo

Provavelmente que você tenha ouvido falar sobre os evils de grande união. Portanto, você sempre se esforçar para manter seus componentes como flexível e possível, certo? Sim, à direita.

Rigidez é mais difícil do que grande união e muitas vezes o desempenho não é tão boa. Você começar com o melhor de intenções, mas acaba perguntando se o benefício vale o custo. Por que apresente uma inserção de dependência e de interface quando você apenas pode criar uma instância da classe e chamar o método diretamente? Por que criar uma abstração com objetos personalizados mapeados para o banco de dados em vez de preencher uma DataTable e os passa ao redor?

Para piorar pior, você não geralmente achar a dificuldade de grande união até muito mais tarde. A curto prazo, você obter algumas eficiência e fazer o trabalho, mas a longo prazo evoluindo o aplicativo pode se tornar quase impossível.

Se você tiver sido criando software para qualquer hora, provavelmente entendeu compensações de união bastante bem quando se trata de módulos de uma camada. Quando você tiver módulos que trabalharem juntos, às vezes, grande união é a escolha certa, mas em outros casos, os componentes necessário a serem mantidos no comprimento do braço entre si para que você pode conter o efeito de onda das alterações para o aplicativo.

Quando se trata de divisão de partes do seu aplicativo em camadas separadas, o significado de união se torna muito maior. O motivo para isso é simples. Camadas sempre não são alterados na mesma taxa. Se você tiver um serviço que é consumido por muitos clientes e você não pode garantir que todos os clientes irão atualizar por demanda, em seguida, é melhor Verifique se que você pode alterar esse serviço sem ter que alterar os clientes. Caso contrário, irá encontrar um problema que às vezes é chamado shearing taxas de alteração. Imagine o aplicativo sendo extraído em duas direções diferentes até que é forçada copiou separadas.

O truque é identificar quais partes do aplicativo podem ter diferentes taxas de alteração e quais partes estão intimamente ligadas ao outro. Primeiro, considere o limite entre o banco de dados e o mid-tier. Conforme o crescimento de seu aplicativo, há uma boa chance de que você terá que ajustar o banco de dados para melhorar o desempenho, e se você nunca compartilhar o banco de dados entre vários aplicativos, há uma chance muito boa que você desejará evoluir mid-tier um aplicativo sem alterar o outro. Felizmente, uso a estrutura de entidades já ajuda aqui porque seu sistema de mapeamento fornece uma abstração entre o seu código mid-tier e o banco de dados. As mesmas perguntas devem ser consideradas entre o mid-tier e o cliente.

Um exemplo de particularmente doloroso e comuns deste antipadrão em ação é uma arquitetura que usa a adaptadores de tabela para recuperar dados do banco de dados e serviços da Web que trocar conjuntos de dados com o cliente. O adaptador de tabela move os dados em um DataSet com o mesmo esquema (assim fortemente união o banco de dados para o mid-tier) e, em seguida, o serviço da Web troca que mesmo DataSet com o cliente (assim fortemente união o mid-tier para o cliente). Esse tipo de sistema é fácil criar — há ferramentas do Visual Studio que levá-lo no processo de perfeitamente. Mas se você criar um sistema dessa forma, alterações em qualquer parte do sistema são provavelmente ripple para todas as outras partes.

Anti-Pattern 2: requisitos de estáticos supondo que

Falando de alterações para o sistema, às vezes, você criar em torno de uma suposição que requisitos permanecerão estáticos, mas há dois casos onde o requisitos de alteração têm um impacto significativo especialmente. Um vem trate o cliente como confiável e o outro ocorre quando o serviço mid-tier assume que o cliente será implementado usando uma tecnologia específica.

Embora seja improvável que os limites de confiança serão alterado inesperadamente, quando se trata de integridade de dados, segurança e confiança, as conseqüências de Obtendo-errado são apenas muito grandes. Se você executar validação somente no cliente, por exemplo e na relação de confiança mid-tier que os dados que receber são OK para enviar diretamente para o banco de dados sem revalidação, a chance de que algo será eventualmente errado é muito maior do que você imagina. Até mesmo saber que o serviço é executado somente dentro de sua intranet não é suficiente manter as informações seguras. Alguém pode criar outro cliente usando o mesmo serviço ou modificar o primeiro cliente para chamar o serviço de um caminho código diferente que ignora a validação. Quem sabe o que poderia acontecer.

Além disso, depois você tiver um serviço, é provável que código regular a ser usado de maneiras que não previu que — muito para que o aprenda algo mais geralmente aceitos seja que você sempre deve validar e impor algum grau de segurança a mid-tier mesmo que pode significar controle de acesso de validação ou executar mais de uma vez.

O segundo problema, bloquear o cliente em uma tecnologia específica, é ainda mais provável é um problema. Tecnologias sempre alterar. Se um aplicativo sobrevive longa o suficiente, algo acontece que força os ajustes de tecnologia e os clientes são especialmente suscetíveis. Você pode inicialmente criar seu aplicativo como um aplicativo de área de trabalho do cliente rich e, em seguida, encontrar mais tarde que você precisa movê-lo para um telefone celular ou Silverlight. Se que estivessem o caso, e você criou seu serviço para conjuntos de dados do exchange, em seguida, surgery principal seria ser necessária para que o serviço e todos os clientes existentes.

Anti-Pattern 3: concorrência Mishandled

Embora não haja uma desvantagem de grande união de troca de conjuntos de dados, o concorrência é uma área complexo, mas-importante que o DataSet manipula bem. Infelizmente muitos desenvolvedores não compreendem as diferenças de gerenciamento de concorrência, e para tornar as coisas pior, um erro com a concorrência é o tipo de problema que geralmente só aparece quando o aplicativo estiver na produção. Se tiver sorte, ele será manifesto como uma falha óbvia. Caso contrário, poderá causar danos aos dados durante um longo período de tempo sem ser detectado.

Em seu núcleo, gerenciamento de concorrência é bem simples: garante integridade de dados mesmo se dois clientes tentam modificar os mesmos dados aproximadamente ao mesmo tempo. Leitores particularmente attentive serão Observe que esses problemas também surgirem em casos em que são não relacionados a várias camadas, mas problemas de concorrência são particularmente relevantes para os designs de fileiras de estrutura de entidades, porque o tratamento da estrutura de entidades de cenários de n camadas cria desafios de concorrência exclusivo.

Para a maioria dos aplicativos, a técnica de gerenciamento de concorrência de escolha é simultaneidade otimista. Embora muitos clientes podem acessar o banco de dados simultaneamente, o número de vezes quando a mesma entidade exata é modificada de maneiras conflitantes é muito pequeno. Para que você assumir tudo será descobrir, mas tomar medidas para detectar se algo der errado.

Detecção é orientada por uma ou mais propriedades, coletivamente chamadas o token de concorrência, que mudam sempre que qualquer parte da entidade é alterado. Quando o aplicativo lê uma entidade, ele salva o valor do token de concorrência. Posteriormente, quando ele deseja gravar essa entidade de volta para o banco de dados, ele primeiro verifica verificar que o valor do token de concorrência no banco de dados é o mesmo agora ele estava quando a entidade foram originalmente lidos. Se for, a atualização continuará. Caso contrário, a atualização é interrompida e lança uma exceção.

A estrutura de entidades oferece suporte a concorrência otimista controle transparente o valor original de tokens de concorrência quando as entidades são consultadas e verificação de conflitos antes para atualizações de banco de dados. O problema com aplicativos de n camadas é que esse processo funciona transparente somente desde que uma única instância de ObjectContext é usada para controlar a entidade a partir do momento que ele é consultado até o momento que SaveChanges é chamado. Se você serializar entidades de uma camada para outra, o padrão recomendado é manter o contexto ao redor o mid-tier somente longo o suficiente para uma chamada de método de serviço single. As chamadas subseqüentes serão rotação backup de uma nova instância do contexto para concluir cada tarefa. (Criando uma nova instância de contexto para cada operação de serviço é uma recomendação importante no seu próprio direita, a propósito. Para obter mais informações, consulte Anti-Pattern # 4: serviços com monitoração de estado.)

Depois que os desenvolvedores começam a aprender como as APIs do Framework entidade funcionam para esse tipo de operação desconectado — desconectado no sentido de que as entidades são desconectadas do contexto após a consulta, enviadas para outra camada e, em seguida, re-connected quando é hora de salvar — há uma tendência a um padrão desagradável se enquadram:

  1. Consultar a entidade e serializá-lo para o cliente. Neste ponto, valor atual do token de concorrência é o mesmo que o valor original e que é o único valor enviado ao cliente.
  2. O cliente recebe a entidade, faz alterações e envia uma versão modificada da entidade volta para o mid-tier.
  3. Como nem o cliente quanto o serviço explicitamente mantidos controle do token de concorrência ou quais propriedades foram modificadas, as consultas do serviço o banco de dados para obter o estado atual da entidade em um contexto recém-criado, em seguida, compara valores entre a entidade atual do banco de dados e a enviados de um cliente.
  4. O serviço chama SaveChanges, que executa otimista verificações de simultaneidade ao persistir as alterações.

Você fez ver o problema? Na verdade, existem dois problemas.

Primeiro, sempre que uma entidade for atualizada, ele deve ser ler do banco de dados duas vezes — uma vez quando ele é primeiro consultado e um segundo direito de tempo antes da atualização — que cria uma carga extra significativa no sistema.

Segundo e mais importante, o "valor original" usado pela estrutura de entidades para verificar se a entidade foi modificada no banco de dados proveniente a segunda consulta em vez do primeiro. Ou seja, ele vem da consulta que acontece direita antes da atualização. Portanto, o resultado é que a verificação de simultaneidade otimista feita por estrutura de entidades quase nunca falhará. Se alguém modificar a entidade entre a primeira consulta e o segundo, o sistema não detectará o conflito porque o valor usado para a verificação de simultaneidade é de após a modificação do outro cliente em vez de antes.

Ainda há uma pequena janela (entre a segunda consulta e a atualização) quando a verificação de simultaneidade otimista pôde detectar um problema, portanto, você ainda terá que escrever seu programa para manipular a exceção, mas você será não realmente ter protegido seus dados contra corrupção.

O padrão correto é para fazer uma cópia da entidade em cliente e enviar novamente a versão original não modificado e a versão modificada ou para gravar o cliente de forma que ela não modifica o token de concorrência. Se o token de concorrência é atualizado por um disparador de servidor ou automaticamente porque é um número de versão de linha (provavelmente a melhor planeja assim mesmo), será não há motivo para modificá-lo no cliente. O valor atual da propriedade pode ser deixado inalterado e usados como armazenamento para o valor original.

Essa é uma abordagem de som razoavelmente porque se um erro no cliente faz com que o valor a ser modificado acidentalmente, é muito improvável que você obterá um sucesso false. Esse erro pode causar você obtenha uma falha de false, mas que é muito mais aceitável de sucesso false.

Para fazer essa abordagem funcionar, quando o mid-tier recebe a entidade do cliente, você precisará anexá-lo ao contexto e, em seguida, examine suas propriedades, marcando-las manualmente como modificadas. Em ambos os casos, entretanto, você irá corrigir ambos os problemas com o antipadrão ao mesmo tempo. Você não irá consultar o banco de dados duas vezes e a verificação de simultaneidade se baseará no valor correto do token (da consulta inicial) em vez de um valor posterior.

Anti-Pattern 4: serviços com monitoração de estado

Fornecido a facilidade comparativa de desenvolvimento de soluções de cliente-servidor, a próxima antipadrão vem backup quando os desenvolvedores tentarem simplificar as coisas, mantendo o contexto ao redor em várias chamadas de serviço. Isso parece bom primeiro porque ele sidesteps os problemas de concorrência. Se você mantiver o contexto de funcionamento on the mid-tier, em seguida, ele conterá os valores de entidade original corretos. Quando você receber uma entidade volta do cliente, você pode comparar a entidade atualizada com a versão da entidade no contexto e aplicar as alterações conforme apropriado. Quando você salva a entidade, será feita a verificação de simultaneidade correto e nenhuma consulta de banco de dados adicional é necessária.

Embora essa abordagem parece fácil na superfície, há inúmeros problemas lurking. Gerenciar o tempo de vida do contexto pode obter complicado rapidamente. Quando você tiver vários clientes chamando os serviços, você precisará manter um contexto separado para cada cliente ou risco colisões entre eles. E mesmo se você resolver esses problemas, você irá terminar com problemas de desempenho principais.

Esses problemas de escalabilidade não são apenas o resultado de amarrando recursos do servidor para cada cliente. Além disso, você precisará se proteger contra a possibilidade de que um cliente pode iniciar uma unidade de trabalho, mas nunca concluí-la, criando um esquema de expiração. Além disso, se você decidir que você precise escala sua solução check-out, introduzindo um farm com diversos servidor mid-tier, você terá que manter afinidade de sessão para manter um cliente associado ao mesmo servidor em que a unidade de trabalho começou.

Muito esforço e tecnologia especializada tem sido inutilizável no endereçamento esses problemas quando, na verdade, a melhor solução é evitá-los totalmente, mantendo suas implementações de serviço mid-tier sem monitoração de estado. Sempre que for feita uma chamada de serviço, o mid-tier deve girar os recursos necessários, lidar com a chamada e solte todos os recursos específicos para essa chamada. Se algumas informações precisam ser mantido para uma unidade de trabalho que se estende em várias chamadas de serviço, em seguida, que informações devem ser mantidas o cliente em vez do mid-tier portanto não há nenhuma afinidade de sessão, não precisam para expirar essas unidades de trabalho e nenhum servidor de recursos em use para um determinado cliente entre o serviço chama.

Fazendo-se anti-Pattern nº 5: duas camadas passar três

Outro antipadrão que encontrar com bastante freqüência também é uma tentativa para simplificar esse processo. Geralmente ele aparece como uma solicitação de algo como "por que não é possível fazer a estrutura de entidades serializar consultas em camadas?" seguido quase imediatamente, " Ah, e enquanto você estivê-la, pode oferecer suporte atualizações início de outra camada bem? "

Eles são provavelmente recursos que Microsoft pode adicionar a estrutura de entidades, mas se você parar e pense-los por um minuto em sabendo dos outros problemas que têm discutido, você precisará pergunta se essa é uma boa idéia.

Se você pode criar um ObjectContext do Framework de entidade na camada cliente, executar qualquer consulta de estrutura de entidades para carregar entidades nesse contexto, modificar essas entidades e então ter SaveChanges enviar uma atualização do cliente por meio a mid-tier para o servidor de banco de dados — se você poderia fizer tudo o que, em seguida, por que precisa o mid-tier em todos os? Por que não basta expor o banco de dados diretamente?

Lembre-se primeira lei do Fowler de objetos distribuídos. Tenha em mente que a única vez que esse tipo de arquitetura faz sentido é quando ela for realmente, realmente necessária. Se você realmente precisar dela, em seguida, você precisa melhor segurança ou a capacidade de dimensionar com vários servidores ou alguma outra coisa sugiro será realmente não ser resolvido, introduzindo um mid-tier que é simplesmente um fino proxy para o banco de dados. Você pode usar essa técnica para subverter a uma restrição colocada em você por uma diretiva corporativa, mas ele dificilmente captura o espírito de um aplicativo com várias camadas. Minha sugestão é investir ou na criação de um aplicativo de n camadas para atender a uma necessidade específica ou, se você puder obter imediatamente com ele, evitar fileiras completamente.

Anti-Pattern nº 6: Undervaluing simplicidade

Isso me leva para a última antipadrão de n camadas. O nome de evitar todos os antipadrões que foi discutidos anteriormente, é fácil decidir o que você precisará criar o mais cuidadosamente arquitetado, multi-tier, totalmente separada, revalidar, super design que podem surgir com. Em seguida, você pode passar todos os seu tempo de criação de infra-estrutura e nenhum de seu tempo de entrega, na verdade, valor para os usuários.

É importante pensar sobre seus objetivos e considere se for necessário a investimento em fileiras requer. É simples. Às vezes, um aplicativo de duas camadas é apenas a coisa. Às vezes, você precisa mais camadas que isso, mas tudo o que está sob seu controle e confiável ou ter um AJAX, Silverlight ou clique em - depois de cliente que automático implanta para que você não precisem se preocupar shearing taxas de alteração.

Se você pode fazer o problema mais simples, fazê-lo. Colocar em todo o esforço para a solução completa se você deve, mas por mesmo token, verifique se você colocar em suficiente esforço para realizar o trabalho de modo que atenda aos seus objetivos.

Danny Simmons é gerente de desenvolvimento para a equipe de estrutura de entidades na Microsoft. Você pode ler mais suas opiniões sobre a estrutura de entidades e outras entidades noblogs.msdn.com/dsimmons/.