Categoria: Arquitetura de Software

  • Funcionalidades no Centro da Arquitetura do Software

    Funcionalidades no Centro da Arquitetura do Software

    Resumo

    O artigo discute como organizar software não apenas por categorias técnicas, como Controller, Model e Service, mas também pelas funcionalidades e capacidades que o sistema oferece.

    A partir de exemplos de organização por categorias técnicas e por módulos, apresenta os benefícios e custos de colocar funcionalidades no centro da estrutura, mostrando que as responsabilidades técnicas continuam existindo, mas passam a ser agrupadas dentro de módulos com significado mais próximo do sistema.

    O problema de organizar software por tecnologia

    Nos dias de hoje, grande parte dos sistemas web modernos possui seus arquivos e códigos organizados conforme delimitações técnicas e, muitas vezes, de acordo com a estrutura do framework utilizado. Essa é uma forma de organização muito comum, principalmente em linguagens e ecossistemas de desenvolvimento web como PHP, Java e similares.

    É muito provável que desenvolvedores que trabalham ou já trabalharam com esses ecossistemas tenham se deparado com uma estrutura de organização de arquivos por pastas semelhante à apresentada abaixo:

    É muito comum encontrar nesses softwares a construção e organização dos arquivos seguindo camadas técnicas. A mais comum organização dessas divisões é a estruturada no padrão arquitetural MVC (Model, View e Controller). Isso ocorre muito porque muitos sistemas utilizam-se de frameworks nos quais são mais adequados, por padrão, a aplicar esse padrão arquitetural.

    Esse tipo de organização tem como uma de suas vantagens a simplicidade, no qual se bem construída, permite escalar para desenvolver sistemas grandes e complexos. No contrário, uma das maiores desvantagens desse tipo de organização é o entendimento das funcionalidades por quem está lendo e dando manutenção.

    A seguir vamos aprofundar um pouco mais nas vantagens e desvantagens da organização por camadas técnicas. Também vamos entender sobre como seria organizar o software por funcionalidade ao invés de camadas técnicas.

    A funcionalidade como unidade de organização

    Existe outra forma de organizar os arquivos de um software: em vez de utilizar apenas categorias técnicas, podemos organizá-los também de acordo com cada funcionalidade ou capacidade relevante do sistema.

    A estrutura abaixo demonstra, de forma simples, como essa organização poderia ser feita:

    A partir do exemplo proposto, temos uma organização dos arquivos baseada no que o sistema oferece em termos de funcionalidades e capacidades.

    É importante observar que as responsabilidades técnicas não desaparecem. Dentro de um módulo ou capacidade como Customer, ainda podem existir diretórios para Application, Domain, Infrastructure e Presentation.

    O que muda, no entanto, é a forma como essas responsabilidades são organizadas. Em vez de começarmos pela categoria técnica de cada arquivo, começamos pelo módulo ao qual aquela funcionalidade ou responsabilidade pertence.

    Uma das possíveis vantagens dessa abordagem é justamente facilitar a compreensão do sistema. Quem precisa ler ou dar manutenção no código pode encontrar, inicialmente, um contexto relacionado à capacidade ou funcionalidade que está sendo desenvolvida ou modificada.

    Por outro lado, essa organização também possui um custo. Construir um sistema com funcionalidades e capacidades como centro exige mais disciplina e cuidado na definição dos módulos. É necessário evitar módulos excessivamente específicos ou genéricos demais, de forma que suas responsabilidades permaneçam claras.

    O que muda quando fazemos isso?

    A partir de alguns exemplos concretos, podemos entender melhor as diferenças entre as duas formas de organização apresentadas anteriormente.

    Abaixo temos um exemplo mais estruturado da organização de um software por categorias técnicas para uma capacidade de gerenciamento de clientes.

    Nessa abordagem, em que os arquivos estão separados por categorias técnicas como Service, Controller e Model, podemos observar algumas características:

    • A quantidade de arquivos pode ser menor;
    • Dependendo da arquitetura adotada, um Model pode concentrar tanto informações relacionadas aos dados persistidos quanto comportamentos relacionados a esses dados;
    • Diferentes responsabilidades, como operações de CRUD, persistência, validações e registros de log, podem acabar concentradas em poucos componentes, dependendo da implementação;
    • Para um software com poucas funcionalidades ou funcionalidades simples, essa estrutura pode funcionar muito bem;
    • À medida que a quantidade e a complexidade das funcionalidades aumentam, alguns componentes podem crescer e se tornar mais difíceis de compreender, manter e testar.

    Agora temos outro exemplo, mais estruturado, da organização de um software por capacidades e funcionalidades relacionadas à gestão de clientes.

    Nessa abordagem, podemos observar que:

    • Existe uma quantidade maior de arquivos;
    • Uma maior quantidade de arquivos, cada um com um propósito mais bem definido, permite dividir melhor as responsabilidades de cada funcionalidade ou capacidade desenvolvida no software;
    • Em vez de a primeira divisão dos arquivos ser baseada em categorias técnicas como Controller, Model e Service, temos uma primeira divisão baseada em um significado mais próximo do sistema, como o módulo Customer;
    • Dentro desse módulo, ainda podemos utilizar divisões como Application, Domain, Infrastructure e Presentation;
    • Esse tipo de estrutura pode ser interessante à medida que a quantidade e a complexidade das funcionalidades aumentam;
    • Desenvolver e manter esse tipo de estrutura exige uma disciplina maior para evitar problemas como a criação excessiva de módulos ou uma especialização desnecessária;
    • Apesar de possuir uma complexidade inicial maior, uma estrutura bem definida pode tornar o sistema mais fácil de compreender e modificar.

    A capacidade de gerenciar clientes está presente nas duas abordagens e pode funcionar muito bem em ambas. O objetivo não é afirmar que uma forma de organização é universalmente melhor do que a outra, mas observar como diferentes formas de organização podem afetar a compreensão e a evolução do software.

    Os exemplos utilizam arquivos PHP apenas para facilitar a representação, mas isso é irrelevante para o conceito apresentado. A mesma ideia poderia ser aplicada a sistemas desenvolvidos em outras linguagens e ecossistemas.

    Funcionalidade não significa ausência de arquitetura

    Estruturar software utilizando capacidades e funcionalidades como centro da organização não significa abandonar a arquitetura ou as responsabilidades técnicas.

    As responsabilidades continuam existindo. O que muda é principalmente a forma como elas são agrupadas dentro do sistema.

    Responsabilidades técnicas

    Por exemplo, em uma abordagem que utiliza MVC ou uma estrutura semelhante, podemos encontrar responsabilidades como:

    View

    Responsável pela apresentação das informações e pela interação com o usuário.

    Controller

    Responsável por receber uma requisição e coordenar a execução da operação correspondente. Dependendo da arquitetura adotada, pode delegar regras e operações para outros componentes.

    Model

    Pode representar os dados e comportamentos relacionados a eles, além de possuir responsabilidades relacionadas à persistência, dependendo da implementação adotada pelo framework e pela arquitetura da aplicação.

    Service

    Pode ser utilizado para concentrar determinadas operações ou regras que não se encaixam adequadamente em outros componentes.

    Essas responsabilidades podem variar de acordo com o framework e com as decisões arquiteturais adotadas em cada sistema.

    Responsabilidades dentro de um módulo

    Quando organizamos o software primeiro por capacidades e funcionalidades, podemos manter diferentes responsabilidades dentro de cada módulo.

    Por exemplo:

    Application

    Contém os casos de uso e a orquestração das operações oferecidas pelo módulo.

    Domain

    Contém conceitos e regras diretamente relacionados ao funcionamento do módulo.

    Entidade

    Representa um conceito relevante para o módulo e seus respectivos dados e comportamentos.

    Repositório

    Pode definir contratos relacionados à persistência ou recuperação de dados utilizados pelo módulo.

    Infrastructure

    Contém implementações concretas de contratos e integrações necessárias para o funcionamento do módulo.

    Presentation

    Contém os componentes responsáveis por expor as funcionalidades do módulo para seus consumidores, como controllers, páginas ou endpoints.

    Nesse modelo, as responsabilidades técnicas continuam existindo. A diferença é que elas passam a estar organizadas dentro do contexto de um módulo, que representa uma capacidade ou conjunto de funcionalidades do sistema.

    Principais Camadas Semânticas para Capacidades e Funcionalidades

    Na organização do software por capacidades e funcionalidades, existem algumas camadas ali com grande significado semântico, como:

    Application

    Guarda os casos de uso do módulo

    Domain

    Contém todas as funcionalidades e interfaces relacionadas diretamente ou indiretamente as regras de negócio

    Entidade

    Guarda a entidade daquele domínio

    Repositório

    Guarda as interfaces relacionadas a persistência daquela entidade

    Infrastructure

    Implementação concreta dos contratos dos contratos (interfaces) definidos na camada de Domínio

    Presentation

    Camada que guarda as funcionalidades relacionadas ao consumo da Application e também responsável por exibir os dados na tela para o usuário

    A arquitetura como expressão da mudança

    É possível pensar e conceber a arquitetura de um software de forma que ela vá além de uma simples divisão em categorias técnicas ou da estrutura inicialmente proposta pelo framework utilizado.

    Um software construído colocando capacidades e funcionalidades no centro de sua organização pode favorecer aspectos como compreensão, modularidade, testabilidade e definição de fronteiras entre diferentes partes do sistema.

    Isso não significa que essa abordagem seja adequada para todos os sistemas. Uma aplicação pequena e simples pode não justificar a complexidade adicional de uma estrutura mais elaborada.

    Existe também um custo: definir módulos, estabelecer suas responsabilidades e manter essa organização exige mais decisões e disciplina, principalmente no início do projeto.

    No fim, talvez a questão mais importante não seja escolher uma única forma correta de organizar software, mas entender o que queremos que a arquitetura torne mais fácil de compreender e modificar.

    Se as mudanças do sistema acontecem principalmente em torno de suas funcionalidades e capacidades, pode fazer sentido que essas mesmas funcionalidades também tenham um papel central na forma como organizamos seu código.