CONTEXTO
Alcançar uma clara definição de fronteiras de um sistema é um dos grandes desafios no Design Arquitetural de um software. Nessa etapa, são concebidos artefatos como módulos, componetes e seus conectores, bem como, as estruturas de alocação que satisfazem um conjunto de requisitos que, mediante a determinadas expectativas de qualidade, respondam às demandas do negócio.
Nesse contexto, o papel do desenvolvimento é "preencher" as lacunas dessas estruturas de software com comportamentos (ricos) do domínio. Para isso, equipes de desenvolvimento têm adotado métodos como Business Process Management (BPM), Domain-Driven Design (DDD), Business Capability Analysis etc, para identificar possíveis fronteiras e estruturas que representem o domínio do negócio.
Contudo, a adoção desses métodos, por si só, não garante que desenvolvedores implementem esses comportamentos de negócio de forma adequada ao ponto que eles sejam livres de efeitos colaterais e fáceis em sua manutenção. Afim de ilustrar as práticas do Design by Contract, o código a seguir é uma representação de um software que será adaptado ao longo desse post para atender os princípios desse conceito.
// Vineyard is a premium wine retailer that caters to both online shoppers and visitors to its physical stores,
// offering an exquisite selection of wines from around the globe.
namespace Vineyard.Orders;
public class OrderService
{
private const string Available = "AVAILABLE";
private const string InTransit = "IN_TRANSIT";
private const string Ecommerce = "ECOMMERCE";
private const string BrickAndMortaStore = "BRICK_AND_MORTA_STORE";
public Order RegisterOrder(OrderRegistation command)
{
var customer = CustomerRepository.GetById(command.CustomerId);
if (customer is null) throw new ArgumentNullException(nameof(customer));
var isOfLegalAge = DateTime.Now.Year - customer.Birthday.Year >= 18;
var product = ProductRepository.GetBySku(command.Sku);
var isValidStatus = product?.Status is Available or InTransit;
var isNotExpired = product is not null &&
product.DueDate < DateTime.Now;
var isValidWinery = command.Plataform switch
{
BrickAndMortaStore => product is not null &&
CategoryRepository.AccreditedWineries().ContainsKey(product.CategoryId),
Ecommerce => product is not null &&
CategoryRepository.AccreditedWineries().ContainsKey(product.CategoryId) &&
!CategoryRepository.WineriesBlockedFromEcommerce().ContainsKey(product.CategoryId),
_ => true
};
var order = new Order(customer, product)
{
IsProcessableOrder = isOfLegalAge && isValidStatus && isNotExpired && isValidWinery
};
//TODO : Register in Database
return order;
}
}
Exemplo. código C#
O código C# apresentado anteriormente ilustra uma aplicação da Vineyard, uma loja fictícia especializada na venda de vinhos importados. Nesse código há uma classe chamada OrderService
que impelementa o método RegisterOrder(OrderRegistation command)
, cuja responsabilidade é registrar a entidade Order
. Para isso o serviço faz uso das seguintes regras:
- O
Pedido
deve conter umCliente
- O
Cliente
deve ter idade igual ou superior a 18 anos - O
Pedido
deve conter umProduto
- O
Produto
deve estar no statusAVAILABLE
ouIN_TRANSIT
- O
Produto
não pode estar vencido - Quando a venda for realizada pela loja física, o
Produto
deve pertencer a umaVinicula
credenciada. - Quando a venda for realizada pelo e-commerce, o
Produto
, além de pertencer a umaVinicula
credenciada, não pode ser de umaVinicula
bloqueada para esse canal.
Apesar de ilustrativo, esse código basea-se em um caso bem próximo de uma implementação real, no qual as regras de negócio estão escritas de forma imperativa em classes de serviço. Isso não é algo raro, e pode ser identificado em diversos sistemas de domínios diferentes.
Numa perspectiva do DDD, os autores do livro Patterns, Principles, and Practices of Domain-Driven Design, Scott Millett e Nick Tune, afirmam que:
people new to DDD tend to model too much behavior. They innocently by into the fallacy that DDD is about modeling the real world. Subsequently, they try to model many real-world behaviors of an entity...
O código
DESIGN BY CONTRACT
Corretude
A Confiabilidade de um sistema está na sua capacidade de executar, de forma consistente, suas funções conforme esperado. A corretude é uma de suas características da
Especificação
Assertation
Pré-condições e Pós-condições
No capítulo "DESIGN BY CONTRACT: BUILDING RELIABLE SOFTWARE" do seu livro "Object-Oriented: software construction", Bertrand Meyer descorre sobre como expressar especificações, asserções
CONTEXTOS FILOSÓFICOS
Esta é uma leitura opcional no texto. Embora possa oferecer insights interessantes e uma compreensão mais profunda do tema em discussão, reconhecemos que nem todos os leitores têm interesse ou tempo para se aprofundar nesses aspectos. Portanto, sinta-se à vontade para explorar essa seção conforme sua conveniência e interesse pessoal.
Passou-se um pouco mais de um século que as reflexões de Frege e Boole sobre inauguraram uma nova perpectiva matemática e filosófiica sobre a lógica
.
As regras de negócio em seu código escondem um segredo que remonta uma tradição filosófica que atravessou milênios. Como velhas ruínas, as validações escritas em linguagem de programação, envolvidas por métodos e funções, tem em seu coração os princípios da lógica
Referencias
- https://softwarehut.com/blog/tech/design-patterns-rules-engine
- https://www.michael-whelan.net/rules-design-pattern/
- https://app.pluralsight.com/ilx/video-courses/48850dfa-8620-48b9-b107-9db26d07061f/7c537ceb-2b27-4a3e-8007-ae787459c4ef/a0167771-dc9a-46e2-b2d5-73c66fde13b3
- https://deviq.com/design-patterns/rules-engine-pattern
- https://blogs.perficient.com/2017/09/17/10-business-rule-patterns-in-the-digital-transformation-and-cognitive-era/
- https://martinfowler.com/bliki/BusinessReadableDSL.html
Top comments (0)