Este artigo foi traduzido por máquina.

Serviços RIA

Padrões empresariais com os Serviços WCF RIA

Michael D. Marrom

Baixe o código de exemplo

Dois anúncios importantes de PDC09 e Mix10 foram a disponibilidade do Silverlight 4 beta e RC, respectivamente. No momento em que você leia isto, a versão completa para Web do Silverlight 4 estarão disponível para download. Juntamente com amplo suporte a impressão, ele inclui suporte para permissões elevadas, webcams, microfones, brindar, acesso de área de transferência e muito mais. Com seu novo conjunto de recursos, 4 do Silverlight está pronta para toe para toe vá com o Adobe AIR como uma estrutura de interface do usuário avançada várias plataformas.

Embora tudo isso excitar me, estou basicamente um desenvolvedor de aplicativos de negócios e uma coisa que eu adoraria é uma maneira simples para colocar meus dados de negócios e lógica em um aplicativo do Silverlight.

Uma preocupação com aplicativos do Silverlight de linha de negócios está se conectando a dados. Nada impede que você criar seu próprio serviço Windows Communication Foundation (WCF) e se conectem a ele no Silverlight 3, mas que deixa muito a desejar, especialmente quando você considera as inúmeras maneiras de que conectar-se aos dados do ASP.NET ou aplicativos de área de trabalho. Enquanto a área de trabalho e aplicativos da Web podem se conectar diretamente ao banco de dados via NHibernate, Estrutura de Entidade (FE) ou ADO.NET brutos construções, aplicativos do Silverlight são separados dos meus dados de “ cloud. ” Eu chamo essa separação chasm dados.

Ultrapassem esse chasm parece extremamente simples primeiro. Obviamente, isso foi feito em algum grau em um número de aplicativos do Silverlight ricos em dados existentes. Mas inicialmente o que parece ser uma tarefa fácil se torna cada vez mais complicado como preocupações mais. Como você controlar alterações durante a transmissão ou encapsular a lógica de negócios nas entidades ao vivo em ambos os lados do firewall?  Como você mantém os detalhes de transmissão de vazar em suas preocupações de negócios?

Ferramentas de terceiros estão surgindo para tratar esses problemas, mas a Microsoft também viu necessárias para fornecer uma solução, portanto, ele introduziu serviços do WCF RIA (anteriormente conhecida como serviços do .NET RIA) ou por questões de brevidade, serviços de RIA. (Você encontrará uma introdução completa a serviços de RIA em “ uma despesa baseados em dados App com o Silverlight 3 de construção ” na edição de maio de 2009 da MSDN Magazine (MSDN.Microsoft.com/magazine/dd695920). Eu tiver sido após ele desde que foi primeiro convidado em um programa beta, fornecendo sugestões à equipe de desenvolvimento e aprender como aproveitar a estrutura em meus próprios aplicativos.

Uma pergunta nos fóruns RIA Services é como RIA serviços se encaixar em arquitetura de práticas recomendada. Sempre fiquei impressionado com os recursos básicos de formulários de dados através dos serviços de RIA, mas definitivamente vi a oportunidade de arquitetar melhor meu aplicativo para que não vazam as preocupações com a estrutura lógica do meu aplicativo.

Apresentando o KharaPOS

Desenvolvi um aplicativo exemplar, KharaPOS, para fornecer um exemplo tangível dos conceitos apresentadas neste artigo. É um ponto de venda aplicativo (POS) implementado no Silverlight 4 usando os serviços de RIA, Estrutura de Entidade e SQL Server 2008. O objetivo final é permitir que o aplicativo de hospedagem na plataforma Windows Azure e SQL Azure, mas o problema de suporte do Microsoft .NET Framework 4 (ou ausência dele) com a plataforma Windows Azure pouco.

Enquanto isso, KharaPOS fornece um exemplo de BOM de usar o .NET Framework 4 para criar um aplicativo real. O projeto é hospedado pelo CodePlex em KharaPOS.codeplex.com. Você pode visitar o site para baixar o código, consulte a documentação sobre e participe da discussão em torno de desenvolvimento do aplicativo.

Eu deve observar que eu emprestado do livro, “ modelos de objeto: Estratégias, padrões e aplicativos, segunda edição ” (Prentice Hall PTR, 1996), por Peter Coad com David Norte e Mark Mayfield, para a maior parte do design e funcionalidade do aplicativo KharaPOS. Falarei sobre um único subsistema do aplicativo, gerenciamento de catálogos (consulte Figura 1).


Figura 1 O modelo de dados de entidade para o gerenciamento de catálogo

Padrões da empresa

Um número de livros excelentes sobre padrões de design para o desenvolvimento de aplicativos corporativos. Um livro que constantemente usar como referência é “ Patterns of Enterprise Application Architecture ” (Addison-Wesley, 2003) por Martin Fowler. Este livro e seu site complementar (martinfowler.com/eaaCatalog/) Fornece um excelente resumo dos padrões de software úteis para desenvolver aplicativos de negócios da empresa.

Alguns padrões no catálogo do Fowler lidar com a apresentação e manipulação de dados e interessante que eles ocupam o mesmo espaço como serviços de RIA. Noções básicas sobre esses dará uma imagem mais clara de como RIA Services podem ser adaptados para atender às necessidades das mais simples para os aplicativos de negócios mais complexos. Falarei sobre os seguintes padrões:

  • Formulários e controles
  • Script de transação
  • Modelo de domínio
  • Camada de serviço do aplicativo

Let’s faça um tour rápido desses padrões. Os três primeiros diz respeito a diferentes maneiras de lidar com a lógica em torno de seus dados. Como você avança por eles, a lógica se move de serem dispersos em todo o aplicativo e repetido conforme necessário, para que está sendo centralizado e com foco.

Formulários e controles

Os formulários-e-controles padrão (ou como me referir a ele, formulários de dados) coloca toda a lógica na interface do usuário. A princípio, isso parece uma boa idéia. Mas para entrada de dados simples e modos de exibição mestre-detalhe, é a abordagem mais direta e simples para obter de interface do usuário ao banco de dados. Muitas estruturas têm suporte intrínseco para esse padrão (Ruby on Rails scaffolding, dados dinâmicos do ASP.NET e são SubSonic três preparam exemplos), portanto não há definitivamente, uma hora e o local para que alguns chamam um antipadrão. Enquanto muitos desenvolvedores dedicada a abordagem de dados do através de formulários para apenas protótipos inicial, há definitivamente usos para ele no finais de aplicativos.

Independentemente de sua opinião sobre o utilitário, não há nenhum negando a simplicidade ou approachability dos formulários sobre dados. Desenvolvimento rápido de aplicativos (RAD) não é chamado porque é entediante. Serviços do WCF RIA traz RAD para Silverlight. Utilizar Estrutura de Entidade, RIA Services e o designer do Silverlight, é possível criar um editor simples de dados do através de formulários com base em uma tabela de banco de dados em cinco etapas:

  1. Crie um novo aplicativo de negócios do Silverlight.
  2. Adicione um EDM (modelo de dados de entidade nova) para o aplicativo Web criado (usando o Assistente para importar o banco de dados).
  3. Adicionar um serviço de domínio ao aplicativo da Web (não se esqueça de criar primeiro para que o EDM é descoberto corretamente) fazendo referência ao modelo de dados.
  4. Use o painel de fontes de dados para arrastar uma entidade exposta por serviços RIA para a superfície de uma página ou controle de usuário no aplicativo do Silverlight (Certifique-se de criar novamente para que ele possa ver o novo serviço de domínio).
  5. Adicione um botão e code-behind para salvar as alterações no formulário ao banco de dados com esta linha simples:
this.categoryDomainDataSource.SubmitChanges();

Agora você tem uma grade de dados simples que pode ser usada para diretamente editar linhas existentes da tabela. Com algumas adições mais, você pode criar um formulário que permite adicionar novas linhas à tabela.

Embora esse padrão tenha sido demonstrado repetidamente, mostrando a vantagem de RAD com os serviços do WCF RIA, é ainda é relevante aqui porque ele fornece uma linha de base para o desenvolvimento com a estrutura. Além disso, como mencionado, este é um padrão válido dentro de aplicativos com base nos serviços de RIA.

Recomendação Como com dados dinâmicos do ASP.NET, o padrão de dados do através de formulários deve ser usado para interfaces de administração simples (como o KharaPOS produto categoria editor), onde a lógica é simples e direto: Adicionar, remover e editar linhas dentro de uma tabela de pesquisa. Mas Silverlight e RIA Services ser dimensionado para aplicativos muito mais complexos, como veremos agora.

Gateway de dados de tabela A abordagem da caixa padrão, fora dos serviços de RIA que abordei apenas aplicativos também podem ser exibidos como uma implementação do padrão de gateway de dados da tabela como apresentados na pág.. 144–151 do Fowler livro. Por meio de dois níveis de indireção (EF mapeamento sobre o banco de dados seguido de mapeamento de serviços de domínio sobre EF), criei um gateway simples para o banco de dados de tabelas usando básico criar, ler, atualizar e excluir operações (CRUD) retornando transferência de dados com rigidez de tipos de objetos (DTOs).

Tecnicamente, isso não se qualifica como um gateway de dados de tabela puro causa de suas camadas duplas de indireção. Mas se squint, parecida com o tabela dados gateway padrão. Para ser honesto, teria sido uma progressão mais lógica para discutir o mapeamento entre os serviços de RIA e padrão de gateway de tabela de dados, porque todos os padrões restantes na lista são padrões de interface de dados, mas formulários sobre dados é principalmente um padrão de interface de usuário. No entanto, senti-lo mais prudente iniciar com o cenário básico e se concentrar na interface do usuário mover de volta para o banco de dados.

Modelo-View ViewModel (MVVM) Embora seja simples de criar um formulário funcional usando formulários sobre dados, ainda há alguns atrito envolvidos. Figura 2, o XAML para o gerenciamento da categoria, ilustra isso.

Figura 2 XAML para o gerenciamento da categoria

<Controls:TabItem Header="Categories">
  <Controls:TabItem.Resources>
    <DataSource:DomainDataSource
      x:Key="LookupSource"
      AutoLoad="True"
      LoadedData="DomainDataSourceLoaded"
      QueryName="GetCategoriesQuery"
      Width="0">
      <DataSource:DomainDataSource.DomainContext>
        <my:CatalogContext />
      </DataSource:DomainDataSource.DomainContext>
    </DataSource:DomainDataSource>
    <DataSource:DomainDataSource
      x:Name="CategoryDomainDataSource"
      AutoLoad="True"
      LoadedData="DomainDataSourceLoaded"
      QueryName="GetCategoriesQuery"
      Width="0">
      <DataSource:DomainDataSource.DomainContext>
        <my:CatalogContext />
      </DataSource:DomainDataSource.DomainContext>
      <DataSource:DomainDataSource.FilterDescriptors>
        <DataSource:FilterDescriptor 
          PropertyPath="Id" 
          Operator="IsNotEqualTo" Value="3"/>
      </DataSource:DomainDataSource.FilterDescriptors>
    </DataSource:DomainDataSource>
  </Controls:TabItem.Resources>
  <Grid>
    <DataControls:DataGrid
      AutoGenerateColumns="False" 
      ItemsSource="{Binding Path=Data,
        Source={StaticResource CategoryDomainDataSource}}" 
      x:Name="CategoryDataGrid">
      <DataControls:DataGrid.Columns>
        <DataControls:DataGridTextColumn 
          Binding="{Binding Name}" Header="Name" Width="100" />
        <DataControls:DataGridTemplateColumn 
          Header="Parent Category" Width="125">
            <DataControls:DataGridTemplateColumn.CellEditingTemplate>
              <DataTemplate>
                <ComboBox 
                  IsSynchronizedWithCurrentItem="False" 
                  ItemsSource="{Binding Source=
                    {StaticResource LookupSource}, Path=Data}"  
                  SelectedValue="{Binding ParentId}" 
                  SelectedValuePath="Id" 
                  DisplayMemberPath="Name"/>
              </DataTemplate>
            </DataControls:DataGridTemplateColumn.CellEditingTemplate>
            <DataControls:DataGridTemplateColumn.CellTemplate>
              <DataTemplate>
                <TextBlock Text="{Binding Path=Parent.Name}"/>
              </DataTemplate>
            </DataControls:DataGridTemplateColumn.CellTemplate>
          </DataControls:DataGridTemplateColumn>
          <DataControls:DataGridTextColumn
            Binding="{Binding ShortDescription}"
            Header="Short Description" Width="150" />
          <DataControls:DataGridTextColumn 
            Binding="{Binding LongDescription}" 
            Header="Long Description" Width="*" />
        </DataControls:DataGrid.Columns>
    </DataControls:DataGrid>
  </Grid>
</Controls:TabItem>

A coluna para a categoria pai na grade de dados é uma caixa de combinação que usa uma lista de categorias existentes para que usuários possam selecionar a categoria pai pelo nome em vez de memorizar a ID da categoria. Infelizmente, o Silverlight não que ele quando é carregado ao mesmo objeto duas vezes dentro da árvore visual. Portanto, eu tive que declara duas fontes de dados de domínio: uma para a grade e outro para a caixa de combinação de pesquisa. Além disso, o code-behind para o gerenciamento de categorias é complicado em vez disso (consulte Figura 3).

Figura 3 Código-behind para gerenciar categorias

private void DomainDataSourceLoaded(object sender, LoadedDataEventArgs e)
{
  if (e.HasError)
  {
    MessageBox.Show(e.Error.ToString(), "Load Error", MessageBoxButton.OK);
    e.MarkErrorAsHandled();
  }
}

private void SaveButtonClick(object sender, RoutedEventArgs e)
{
  CategoryDomainDataSource.SubmitChanges();
}

private void CancelButtonClick(object sender, RoutedEventArgs e)
{
  CategoryDomainDataSource.Load();
}

void ReloadChanges(object sender, SubmittedChangesEventArgs e)
{
  CategoryDomainDataSource.Load();
}

Não vou dar um tutorial completo MVVM aqui — consulte o artigo, “ WPF aplicativos com o Model-View-ViewModel padrão de design ” na edição de fevereiro de 2009 (MSDN.Microsoft.com/magazine/dd419663) para um excelente treatise no tópico. Figura 4 mostra uma maneira de alavancar MVVM dentro de um aplicativo de serviços de RIA.

Figura 4 Categoria de gerenciamento por meio de um modelo de exibição

public CategoryManagementViewModel()
{
  _dataContext = new CatalogContext();
  LoadCategories();
}

private void LoadCategories()
{
  IsLoading = true;
  var loadOperation= _dataContext.Load(_dataContext.
    GetCategoriesQuery());
  loadOperation.Completed += FinishedLoading;
}

protected bool IsLoading
{
  get { return _IsLoading; }
  set
  {
    _IsLoading = value;
    NotifyPropertyChanged("IsLoading");
  }
}

private void NotifyPropertyChanged(string propertyName)
{
  if (PropertyChanged!=null)
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

void FinishedLoading(object sender, EventArgs e)
{
  IsLoading = false;
  AvailableCategories=
    new ObservableCollection<Category>(_dataContext.Categories);
}

public ObservableCollection<Category>AvailableCategories
{
  get
  {
    return _AvailableCategories;
  }
  set
  {
    _AvailableCategories = value;
    NotifyPropertyChanged("AvailableCategories");
  }
}

Como você pode ver, o ViewModel é responsável para inicializar o contexto de domínio e informar a interface do usuário quando uma carga está ocorrendo, junto com as solicitações de interface do usuário para criar novas categorias, salvar as alterações categorias existentes e recarregar os dados do serviço de domínio. Isso deixa uma separação clara entre a interface do usuário e a lógica que orienta a ele. Padrão MVVM pode parecer requer mais trabalho, mas sua beleza próprio revela o tempo que primeiro você tem para alterar a lógica para obtenção de dados na interface do usuário. Além disso, mover o processo de carregamento de categorias para o ViewModel nos permite limpar o modo de exibição significativamente (XAML e code-behind semelhante).

Recomendação Use MVVM para impedir a lógica da interface do usuário complexa congestionar a interface do usuário — ou pior, do congestionar o seu modelo de objeto de negócios.

Script de transação

À medida que começar a adicionar lógica de seu aplicativo, o padrão de dados do através de formulários se torna complicado. Como a lógica sobre o que pode ser feito com os dados está incorporada na interface do usuário (ou em ViewModel,), se você assumir essa etapa irá estar dispersas entre o aplicativo. Outro efeito colateral da lógica descentralizado é que os desenvolvedores podem não estar cientes que já existe funcionalidade específica do aplicativo, que pode levar a duplicação. Isso cria pesadelos quando a lógica é alterado, porque ele precisa ser atualizado em todos os locais (supondo que todos os locais que implementa a lógica tiverem sido catalogados corretamente).

O padrão de script de transação (vi. 110–115 no catálogo do Fowler) fornece algum alívio. Ele permite separar a lógica empresarial que gerencia os dados da interface do usuário.

Conforme definido pela Fowler, o script de transação “ organiza lógica de negócios por procedimentos onde cada procedimento lida com uma única solicitação da apresentação. ” Os scripts de transação são muito mais do que simples operações CRUD. Na verdade, eles ficam na frente do gateway de dados de tabela para lidar com as operações CRUD. Levado ao extremo, um script de transação separado trataria cada recuperação e envio para o banco de dados. Mas, sendo pessoas lógicas, nós sabemos que há um momento e lugar para tudo.

Um script de transação é útil quando seu aplicativo tiver que coordenar uma interação entre duas entidades, como, por exemplo, quando você cria uma associação entre duas instâncias de classes de entidade diferentes. Por exemplo, no sistema de gerenciamento de catálogo, eu significam que um produto está disponível para uma unidade comercial para que seu inventário criando uma entrada de catálogo. A entrada identifica o produto, a unidade de negócios, um SKU do produto e o tempo durante o qual ele pode ser encomendado ambos interna e externamente. Para simplificar a criação de entradas de catálogo, criei um método no serviço de domínio (consulte o seguinte trecho de código) que fornece um script de transação para modificar a disponibilidade de produtos para uma unidade de negócios sem interface do usuário precisar manipular entradas do catálogo diretamente.

Na verdade, entradas do catálogo ainda não estão expostas por meio do serviço de domínio, como mostrado aqui:

public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
  var entry = ObjectContext.CreateObject<CatalogEntry>();
  entry.BusinessUnitId = businessUnitId;
  entry.ProductId = product.Id;
  entry.DateAdded = DateTime.Now;
  ObjectContext.CatalogEntries.AddObject(entry);
  ObjectContext.SaveChanges();
}

Em vez de serem expostas como uma função no contexto de domínio do cliente, serviços de RIA gera uma função da entidade em questão (no produto neste caso) que, quando chamado, coloca uma notificação de alteração no objeto que é interpretado como uma chamada para o método no serviço de domínio no lado do servidor.

Fowler recomenda duas abordagens para implementar o script de transação:

  1. Com comandos que encapsulam as operações e podem ser passados ao redor
  2. Com uma única classe que contém uma coleção de scripts de transação

Fizemos a segunda abordagem aqui, mas não há nada impedindo o uso de comandos. A vantagem de não expor a entrada de catálogo para a camada de interface do usuário é que o script de transação se torna o único meio de criação de entradas do catálogo. Se você usar o comando padrão, a regra será aplicada por convenção. Se um desenvolvedor esquecer que existe um comando, você acabará direito back onde você iniciou com fragmentação lógica e a duplicação.

Outro benefício de colocar o script de transação no serviço de domínio é que a lógica é executada no servidor (como mencionado anteriormente). Se você tiver algoritmos proprietários ou se quiser ter certeza de que o usuário não tenha manipulado maliciosamente seus dados, colocando o script de transação no serviço de domínio é a maneira de ir.

Recomendação Use o script de transação quando a lógica de negócios se torna muito complexa para formulários sobre dados, você deseja executar a lógica de uma operação no lado do servidor ou em ambos.

Business Logic versus UI Logic Fazer várias referências a lógica da interface do usuário em vez de lógica comercial e entanto a diferença pode parecer sutil primeiro, é importante. Lógica da interface do usuário é a lógica preocupada com a apresentação — o que é exibido na tela e como (por exemplo, os itens usados para preencher uma caixa de combinação). Lógica de negócios, por outro lado, é o que o aplicativo propriamente dito (por exemplo, o desconto aplicado a uma compra on-line) de drives. Ambos são aspectos importantes de um aplicativo e outro padrão surge quando eles estão permitidos misturar — consulte o artigo, “ grande bola de lama, ”, Brian Foote e Joseph Yoder (laputan.org/mud).

Passar várias entidades para o serviço de domínio Por padrão, você poderá passar apenas uma entidade para um método de serviço do domínio personalizado. Por exemplo, o método

public void CatalogProductForBusinessUnit(Product product, int businessUnitId)

não funcionará se você tentar usar essa assinatura em vez de um número inteiro:

public void CatalogProductForBusinessUnit(Product product, BusinessUnit bu)

Serviços RIA geraria um proxy de cliente para essa função não porque … bem, essas são as regras. Você pode ter apenas uma entidade em um método de serviço personalizado. Isso não deve representar um problema na maioria das situações, porque se você tiver uma entidade, você tem a sua chave e poderá recuperá-la no back-end novamente.

Let’s dizer apenas, para fins de demonstração, que é uma operação cara para recuperar uma entidade (talvez seja no outro lado de um serviço da Web). É possível informar ao serviço de domínio que você deseja que ele mantenha uma cópia de uma determinada entidade, como mostrado aqui:

public void StoreBusinessUnit(BusinessUnit bu)
{
  HttpContext.Current.Session[bu.GetType().FullName+bu.Id] = bu;
}

public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
  var currentBu = (BusinessUnit)HttpContext.Current.
    Session[typeof(BusinessUnit).FullName + businessUnitId];
  // Use the retrieved BusinessUnit Here.
}

Como o serviço de domínio estiver sendo executado no ASP.NET, ele tem acesso completo a sessão do ASP.NET e o cache, da mesma forma, caso você deseje remover automaticamente o objeto de memória após um determinado período de tempo. Estou realmente usando essa técnica em um projeto em que preciso recuperar dados CRM (gerenciamento) de relação do cliente de vários serviços da Web remotos e apresentá-lo para o usuário de acordo com uma interface de usuário unificada. Uso um método explícito porque alguns dados vale o cache e alguns não.

Modelo de domínio

Às vezes, lógica de negócios se torna tão complexa que até mesmo os scripts de transação corretamente não é possível gerenciá-lo. Freqüentemente, isso aparece como complexa lógica de ramificação de um script de transação ou vários scripts de transação para considerar as nuances de lógica. Outro sinal de que um aplicativo tem superado o utilitário de scripts de transação é a necessidade de atualizações freqüentes para endereço rapidamente às necessidades dinâmicas dos negócios.

Se você percebeu esses sintomas, é hora de considerar um modelo de domínio avançado (vi. 116–124 no livro Fowler). Os padrões abordados até agora têm algo em comum: As entidades são pouco mais do que os DTOs — eles contêm nenhuma lógica (isso é considerado por alguns seja um antipadrão conhecido como modelo de domínio anêmica). Um dos maiores benefícios do desenvolvimento orientado a objeto é a capacidade de encapsular dados e a lógica associada a ele. Um modelo de domínio avançado aproveita desse benefício, colocar a lógica novamente para a entidade no qual ele pertence.

Os detalhes da criação de um modelo de domínio estão além do escopo deste artigo. Consulte o livro, “ design orientado por domínio: Lidar com a complexidade no coração do software ” (Addison-Wesley, 2004), por Eric Evans, ou o livro Coad mencionado anteriormente sobre modelos de objeto grande cobertura sobre esse tópico. No entanto, pode, forneço um cenário que ajuda a ilustrar como um modelo de domínio pode gerenciar alguns dessa pressão.

Alguns clientes KharaPOS desejam examinar o históricas de vendas de determinadas linhas e decidir, em uma base de mercado pelo mercado, se as linhas serão expanded (disponibilizando mais produtos dele), reduzido, Recortar todos juntos ou permanecem inalterados para uma determinada estação do ano.

Já tenho os dados de vendas em outro subsistema do KharaPOS e tudo o mais que preciso é aqui no sistema de catálogo. Eu simplesmente será trazer um modo de exibição somente leitura de vendas de produtos para nosso modelo de dados de entidade como mostra a Figura 5.


Figura 5 Modelo de dados de entidade atualizado com dados de vendas

Agora, tudo que preciso fazer é adicionar a lógica de seleção de produtos para o modelo de domínio. Porque eu estou selecionar produtos para um mercado, eu colocará a lógica na classe BusinessUnit (usar uma classe parcial com uma extensão shared.cs ou shared.vb para informar ao RIA serviços para que esta função ser shuttled ao cliente). A Figura 6 mostra o código.

Figura 6 Lógica de domínio para selecionar produtos para uma unidade de negócios

public partial class BusinessUnit
{
  public void SelectSeasonalProductsForBusinessUnit(
    DateTime seasonStart, DateTime seasonEnd)
  {
    // Get the total sales for the season
    var totalSales = (from sale in Sales
                     where sale.DateOfSale > seasonStart
                     && sale.DateOfSale < seasonEnd
                     select sale.LineItems.Sum(line => line.Cost)).
                     Sum(total=>total);
    // Get the manufacturers for the business unit
    var manufacturers =
      Catalogs.Select(c =>c.Product.ManuFacturer).
        Distinct(new Equality<ManuFacturer>(i => i.Id));
    // Group the sales by manufacturer
    var salesByManufacturer = 
      (from sale in Sales
      where sale.DateOfSale > seasonStart
      && sale.DateOfSale < seasonEnd
      from lineitem in sale.LineItems
      join manufacturer in manufacturers on
      lineitem.Product.ManufacturerId equals manuFacturer.Id
      select new
      {
        Manfacturer = manuFacturer,
          Amount = lineitem.Cost
      }).GroupBy(i => i.Manfacturer);
    foreach (var group in salesByManufacturer)
    {
      var manufacturer = group.Key;
      var pct = group.Sum(t => t.Amount)/totalSales;
      SelectCatalogItemsBasedOnPercentage(manufacturer, pct);
    }
  }

  private void SelectCatalogItemsBasedOnPercentage(
    ManuFacturer manufacturer, decimal pct)
  {
     // Rest of logic here.
  }
}

Realizar uma seleção automática para produtos que transportar através de uma estação do ano é tão simples quanto chamar a nova função no BusinessUnit e procedimentos que funcionam por uma chamada para o SubmitChanges na DomainContext.No futuro, se for encontrado um bug na lógica ou a lógica precisa ser atualizado, eu sei onde procurar.Não só tem centralizada da lógica, que também fiz o modelo de objeto mais expressivo do propósito de.No p.246 do seu catálogo de “ Domain-Driven Design ”, Evans explica por que isso é bom:

Se um desenvolvedor deve considerar a implementação de um componente para usá-lo, o valor de encapsulamento é perdido.Se alguém que não seja o desenvolvedor original deverá inferir a finalidade de um objeto ou a operação com base em sua implementação, esse novo desenvolvedor pode inferir um propósito que a operação ou a classe atende apenas pela oportunidade.Se não for o objetivo, o código pode funcionar para o momento, mas será conceitual base para o projeto ter sido corrompido e dois desenvolvedores estarão trabalhando no cross-purposes.

Para Parafraseando, explicitamente a função de nomeação para sua finalidade e encapsular a lógica (juntamente com alguns comentários para torná-lo a limpar o que está acontecendo), fiz-lo mais fácil para o próximo cara (mesmo que o cara próximo me é cinco meses a partir de agora) para determinar quais acontecendo antes mesmo que eu ir para a implementação.Colocar essa lógica com os dados para o qual tem naturalmente associados aproveita a natureza expressiva das linguagens orientadas a objeto.

Recomendação Use um modelo de domínio quando sua lógica é complexo e gnarly e pode envolver várias entidades por vez.Incluir a lógica com o objeto ao qual ele tem a maioria dos afinidade e forneça um nome significativo, intencional para a operação.

A diferença entre o modelo de domínio e o script de transações nos serviços de RIA Talvez você tenha notado que o script de transação e o modelo de domínio, a chamada foi feita diretamente na entidade.Observe, contudo, a lógica para dois padrões reside nos dois locais separados.No caso do script de transação, chamar a função da entidade apenas serve para indicar para o contexto de domínio/serviço que a função correspondente deve ser chamada no serviço de domínio as próxima alterações em tempo de envio é chamado.No caso do modelo de domínio, é executada a lógica do cliente e compromisso quando enviar alterações é chamado.

Os objetos de consulta e repositório O serviço de domínio naturalmente implementa o padrão do repositório (consulte Fowler, p.322).No WCF RIA Services Code Gallery (Code.msdn.Microsoft.com/RiaServices), a equipe de serviços de RIA fornece um excelente exemplo de criação de uma implementação explícita do padrão sobre o DomainContext.Isso permite maior capacidade de teste do seu aplicativo sem a necessidade de visitas, na verdade, a camada de serviço e o banco de dados.Além disso, no meu blog (azurecoding.NET/blogs/Brownie) Posso fornecer uma implementação do padrão de objeto de consulta (Fowler, p.316) sobre o repositório, que adia a execução no servidor da consulta até enumeração real.

Camada de serviço do aplicativo

Pergunta rápida: O que fazer quando você deseja aproveitar um modelo de domínio avançado, mas Don deseja expor sua lógica para a camada de interface do usuário?É onde o serviço de aplicativo de camada padrão (Fowler, p.133) se torna útil.Depois do modelo de domínio, esse padrão é simples de implementar movendo a lógica do domínio fora do shared.cs para uma classe parcial separada e colocando uma função no serviço de domínio que invoca a função da entidade.

A camada de serviço do aplicativo age como uma fachada simplificada seu modelo de domínio, expondo operações, mas não seus detalhes.Outra vantagem é que os objetos de domínio poderão levar dependências internas sem exigir que os clientes de camada de serviço para levá-los também.Em alguns casos (veja o exemplo sazonais de seleção de produtos mostrado a Figura 6), o serviço de domínio faz uma chamada simples no seu domínio.Às vezes, ele pode coordenar algumas entidades, mas tenha cuidado — muito orquestração transforma novamente em um script de transação e os benefícios de encapsular a lógica dentro do domínio são perdidos.

Recomendação Use a camada de serviço do aplicativo para fornecer uma fachada simples sobre o modelo de domínio e remover o requisito de que camada de interface do usuário precisa assumir dependências de que suas entidades podem demorar.

Crédito extra: O contexto limitado

Nos fóruns RIA, participantes geralmente pedir, “ como eu dividir meu banco de dados muito grande nos serviços de domínio para que ele seja gerenciável? ” Uma questão de acompanhamento é, “ como, manipular entidades precisam existir em vários serviços de domínio? ” Inicialmente pensei que não deve haver a necessidade de fazer essas coisas; serviço de domínio deve agir como uma camada de serviço em seu modelo de domínio e um serviço único domínio deve servir como uma fachada sobre todo o seu domínio.

Durante minha pesquisa deste artigo, no entanto, eu veio entre o padrão de contexto limitada (Evans, p.336), que eu poderia ler sobre antes, mas não havia lembradas quando eu originalmente respondeu a essas perguntas.A premissa básica do padrão é que em qualquer projeto grande será haver vários subdomínios em jogo.Tome como exemplo KharaPOS, onde tenho um domínio para catálogos e separados para vendas.

O contexto limitado permite que esses domínios coexistir paz, mesmo que haja elementos compartilhados entre eles (como vendas, unidade de negócios, produtos e LineItem, que estão na vendas e os domínios de catálogo).Regras diferentes se aplicam às entidades com base em qual domínio está interagindo com eles (de comercialização e LineItem são somente leitura no domínio de catálogo).Uma regra final é operações nunca passam através de contextos.Isso é torna a vida conveniente, porque o Silverlight não oferece suporte a transações em vários serviços de domínio.

Recomendação Use contextos limitados para dividir um grande sistema em subsistemas lógicos.

O PIT de sucesso

Neste artigo, nós vimos como serviços RIA compatível com os padrões de grande empreendimento com muito pouco esforço.Raramente são estruturas tão acessível ao mesmo tempo que também seja flexível o suficiente para oferecer suporte a tudo, desde os aplicativos de entrada de dados de planilha mais simples para os mais complexos aplicativos de negócios sem exigir grande esforço para fazer a transição.Esta é a “ PIT de sucesso ” Brad Abrams mencionado no seu blog, postar, o mesmo nome (blogs.msdn.com/brada/Archive/2003/10/02/50420.aspx).

Mike Brown* é o presidente e co-fundador da KharaSoft Inc.(kharasoft.com), uma empresa de tecnologia especializado em treinamento, desenvolvimento de software personalizado e software como um serviço.Ele é uma especialista em tecnologia realizado com mais de 14 anos de experiência no setor, um destinatário do prêmio MVP em frente e verso, co-fundador do grupo de usuários Indy Alt.NET (indyalt.NET) e do ventilador ursos die-hard!*

Graças ao especialista técnico seguir para revisar este artigo:  Brad Abrams