Martin Fowler é considerado um dos papas na área de arquitetura de aplicações - ele produziu muitos artigos e publicou 16 livros sobre o assunto (ou similares). Escrevendo de uma forma bem prática e didática, Fowler é uma das minhas principais fontes de orientação sobre o tema, e conseguiu sintetizar em 4 padrões de lógica de domínio - onde colocamos as regras de negócio, que podem nos ajudar muito na criação de novos projetos. Veja que a decisão de adotar um ou outro padrão pode nos ajudar a reduzir o trabalho de manutenção futura - Este pode ser o principal benefício, que deve ser avaliado pelo responsável do projeto. Ao final deste artigo, vou explicar um pouco melhor quando é melhor adotar um ou outro padrão - veja que é importante conhecer bem estes padrões, para depois poder escolher.
Mas precisamos antes contextualizar as 3 camadas, bem como suas responsabilidades em um sistema típico, para depois prosseguir na explanação dos padrões de organização da lógica de negócio:
- Apresentação - Camada que contém toda a lógica responsável pela manipulação das interações entre o sistema e o mundo exterior (usuário x sistema). Em resumo, esta camada é responsável por coletar os dados informados pelos usuários, e exibir os resultados dos processamentos realizados nas camadas mais profundas.
- Domínio - A camada de lógica de domínio também é denominada de lógica de negócios. Esta é a camada que funciona como o motor e cérebro do sistema, e faz o que deve ser feito. A lógica de domínio realiza os cálculos sobre os dados informados ou dados armazenados. Ele passa os dados para a camada de persistência, após ter validado a entrada da camada de apresentação.
- Fonte de dados - O propósito desta camada é principalmente comunicar-se com outros sistemas, como bancos de dados, sistemas de envio de mensagens e gerenciadores de transações. Em aplicações web, na maioria dos casos, estes sistemas serão sistemas serão gerenciadores de bancos de dados.
Agora vamos prosseguir. Os padrões para organizar a lógica de negócios são:
O Gama foi concebido adotando um misto entre os três primeiros, mas temos exemplos do uso de todos ao longo dos projetos que já desenvolvemos na IASoft.
Transaction Script (Script transacional)
http://martinfowler.com/eaaCatalog/transactionScript.html
Organiza a lógica de negócio em procedimentos onde cada procedimento trata uma simples requisição da camada de apresentação (interface do usuário);
A maioria das aplicações pode ser pensada como uma série de transações. Cada interação entre a interface e o servidor possui uma certa quantidade de lógica de negócio. Em alguns casos pode ser uma simples exibição de informações do banco de dados. Em outras, pode envolver muitos passos de validação e cálculos.
Um Transaction Script organiza toda esta lógica, primariamente em um simples procedimento, fazendo chamadas diretamente para o banco de dados, ou através de uma camada de abstração. Cada transação terá seu próprio Transaction Script, embora seja comum que tarefas possam ser quebradas em subprocedimentos.
No nosso caso, usamos este padrão como ações das Actions.
Domain Model (Modelo de Domínio)
http://martinfowler.com/eaaCatalog/domainModel.html
Um Modelo de Objetos, de um certo domínio, que incorpora tanto dados quanto comportamentos.
A lógica de negócios pode ser muito complexa. Nem sempre é fácil organizar as regras para atender a diferentes casos e comportamentos. O Domain Model cria uma rede de objetos interconectados, onde cada um representa algum indivíduo significativo, seja grande como uma empresa, ou pequeno como uma simples linha de um formulário.
No nosso caso, são os BOs.
Table Module (Módulo de Tabelas)
http://martinfowler.com/eaaCatalog/tableModule.html
A single instance that handles the business logic for all rows in a database table or view.
One of the key messages of object orientation is bundling the data with the behavior that uses it. The traditional object-oriented approach is based on objects with identity, along the lines of Domain Model (116). Thus, if we have an Employee class, any instance of it corresponds to a particular employee. This scheme works well because once we have a reference to an employee, we can execute operations, follow relationships, and gather data on him.
One of the problems with Domain Model (116) is the interface with relational databases. In many ways this approach treats the relational database like a crazy aunt who's shut up in an attic and whom nobody wants to talk about. As a result you often need considerable programmatic gymnastics to pull data in and out of the database, transforming between two different representations of the data.
A Table Module organizes domain logic with one class per table in the data-base, and a single instance of a class contains the various procedures that will act on the data. The primary distinction with Domain Model (116) is that, if you have many orders, a Domain Model (116) will have one order object per order while a Table Module will have one object to handle all orders.
A Table Module is many ways the middle man between a Transaction Script and a Domain Model. A Table Module tends to organize domain logic with one class per table in the database. While a Transaction Script will have one class Hotel to do reservations a Table Module may have more fine grained classes like Hotel, Booking, Room. Even though these classes may be singular they do not actually represent individual entities and manages whole collections of entities. For example a Booking class would be responsible for mosts actions performed on the bookings database table. Like a Transaction Script a Table Module is a very data centric model approach and usually has a strong connection with the data access layer. Table Module has a few benefits over a Transaction Script. There’s less duplication, domain logic is more organized and structured around domain entities. It works well with simple data access layers. However, Table Module doesn’t give you full power of objects in organizing complex domain logic. It lacks instance to instance relationships, has weak support for polymorphism.
Esta abordagem está muito associada com o modelo de dados, e por isso este deve ser bem definido.
Service Layer (Camada de Serviços)
http://martinfowler.com/eaaCatalog/serviceLayer.html
Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.
Enterprise applications typically require different kinds of interfaces to the data they store and the logic they implement: data loaders, user interfaces, integration gateways, and others. Despite their different purposes, these interfaces often need common interactions with the application to access and manipulate its data and invoke its business logic. The interactions may be complex, involv-ing transactions across multiple resources and the coordination of several responses to an action. Encoding the logic of the interactions separately in each interface causes a lot of duplication.
A Service Layer defines an application's boundary [Cockburn PloP] and its set of available operations from the perspective of interfacing client layers. It encapsulates the application's business logic, controlling transactions and coor-dinating responses in the implementation of its operations.
Escolhendo o padrão
Acredito que o aspecto mais importante para a escolha de um padrão de organização da lógica de negócio, é o da manutenção. Tanto a evolutiva quanto a corretiva:
- Evolutiva - este é o principal aspecto, pois melhorias representam a inclusão de novos componentes, tais como classes e tabelas, bem como novas regras de negócios.
- Corretiva - a correção de bugs ou mesmo ajustes podem afetar outras partes do sistema, mas não devem, de forma alguma, alterar uma regra de negócio inadvertidamente. Por isso é importante saber onde elas estão.
No gráfico abaixo, Martin Fowler apresenta o resultado de uma pesquisa, que demonstra a evolução do esforço de melhoria de um sistema, variando a complexidade da lógica de domínio, nos três principais padrões de organização da lógica de negócios.
É possível perceber que pequenos projetos, com lógica de domínio mais simples, é preferível adotar o padrão Table Module - um gerador de CRUDs pode ser o mais adequado. Entretanto, caso o sistema cresça, a complexidade faz com que aumente o esforço de desenvolvimento (onde ficará uma regra de negócio, que afeta duas tabelas diferentes?).
Pelo gráfico, pode ser um pouco mais trabalhoso iniciar o desenvolvimento adotando o padrão Transaction Script - ou seja, identificar as transações que responderão as demandas do mundo externo. No entanto, o aumento na complexidade do sistema vai representar um esforço ainda maior por parte da equipe.
Por fim, Fowler mostra que usar a lógica centrada em um Domain Model é mais preditiva e linear: demanda um pouco mais de esforço no início, mas após um certo nível de evolução, fica mais fácil de manter do que os demais.
Referências:
- http://martinfowler.com/eaaCatalog/index.html
- http://www.thedeveloperday.com/domain-model-logic-patterns/
- http://techportal.ibuildings.com/2011/10/31/architecture-patterns-domain-model-and-friends/
- http://marcobaccaro.wordpress.com/2010/03/12/domain-model/
