Arquitectura e Design num Software Modularizado
Tags: go, softwareengineering, programming, architecture
Published: 2023-12-30 14:05:16



## Definições Relacionadas
**Arquitectura de Software:** está focada na organização dos componentes de software, suas responsabilidades e relacionamento entre eles.
**Design de Software:** está focado na organização de Classes, Objectos, Métodos, Interfaces, Funções, Traits, Protótipos de acordo a linguagem. Em outras palavras, princípios de Design Patterns.
**Módulos:** são unidades autônomas e independentes do sistema, cada um com uma responsabilidade específica.

## Desafios na Arquitetura Modular de Software
Em um ambiente de software modular, cada módulo pode ser implementado com sua própria arquitetura, constituindo uma tarefa complexa, porém poderosa, pois oferece a flexibilidade de adoptar técnicas distintas conforme a natureza específica de cada módulo.
 
Por exemplo, um módulo que requer operações comuns de **CRUD** pode ser estruturado utilizando o Padrão **MVC**, enquanto outro, que demanda a separação rigorosa entre consultas e comandos, pode adotar o modelo **CQRS**. 

O design da aplicação deve ser concebido de forma a possibilitar a integração harmoniosa dos módulos, gerando uma aplicação coesa.
No contexto de aplicações web, é crucial considerar a integração das rotas de cada módulo, uma vez que, no final, o acesso às funcionalidades ocorrerá por meio de URLs.

## Comunicação entre os módulos
A comunicação entre módulos ocorre através de chamadas de métodos, funções ou variáveis, e a organização em namespaces ou pacotes ajuda a evitar conflitos de nomes. 
Nomes consistentes e dependências explicitamente declaradas são práticas recomendadas para facilitar a navegação e manutenção do código.
A seguir, apresentamos as diferentes formas de comunicação entre módulos:

### 1. Chamada de Métodos, Funções ou Variáveis
   - Módulos: Em muitas linguagens, os módulos podem conter funções, métodos ou variáveis. A comunicação entre módulos geralmente ocorre quando uma função, método ou variável de um módulo é chamada por outro módulo. 

### 2. Nível de Pacotes ou Namespaces
   - **Namespaces**: Em algumas linguagens, como C++, C# e PHP, usam namespaces para organizar código e evitar conflitos de nomes. Os módulos podem ser encapsulados em namespaces, e a comunicação entre módulos de diferentes namespaces é feita referenciando o nome do namespace seguido pelo nome do módulo (por exemplo, namespace.modulo.funcao()).
   - **Pacotes**: E noutras  linguagens, como Java, Kotlin e Go, os módulos são organizados em pacotes. A comunicação entre módulos de diferentes pacotes é realizada usando a notação de ponto (por exemplo, pacote.modulo.funcao()).

### 3. Nomes Consistentes para Facilitar a Navegação do Código
   - É recomendado dar nomes consistentes aos módulos, funções e variáveis para facilitar a leitura e manutenção do código. Isso inclui seguir convenções de nomenclatura, escolher nomes descritivos e usar um estilo consistente em todo o projeto.

### 4. Dependências Explícitas
   - Em sistemas mais complexos, é comum declarar explicitamente as dependências entre módulos. Isso pode ser feito em arquivos de configuração específicos (como em sistemas baseados em pacotes) ou através de anotações especiais no código-fonte.

### 5. Interfaces Bem Definidas
   - Em muitas situações, é benéfico ter interfaces bem definidas entre módulos. Isso facilita a manutenção, a substituição de implementações e o teste de unidade.

## Roteamento dos Módulos
Cada módulo pode seguir a  sua própria arquitectura, desde que as rotas sejam unificadas ao final do processo. Em cada módulo, é necessário carregar todas as rotas presentes nos controllers (ou handlers). O resultado final consiste na consolidação de todas as rotas do módulo.

No nível mais alto da aplicação, as rotas inicializadas por cada módulo precisam ser carregadas. Nesta etapa, é essencial inicializar os controllers.

Os controllers têm a responsabilidade de retornar páginas em formatos diversos, como HTML, XML ou JSON, e de lidar com redirecionamentos. A principal preocupação reside na coesão final das rotas, sendo que o tipo de retorno não afeta a estrutura do software.

A sequência de chamadas das rotas segue a ordem:
- Definição de rotas para cada Controller.
- Carregamento de todos os controllers do módulo.
- Integração das Rotas dos Módulos.


## Estrutura básica de um softwre Modular
**1. Config:** Contém as configurações essenciais, como variáveis de ambiente, configurações de base de dados, engine de templates, sessão e logs. Centraliza as configurações críticas do sistema, facilitando a gestão e a modificação de parâmetros fundamentais.
**2. Helpers:** Responsável pela implementação de funções de auxílio (helpers) que podem ser utilizadas em diversos pontos do sistema.  Encapsula funcionalidades comuns que podem ser compartilhadas entre diferentes módulos.
**3. Middlewares:** Agrupa funções que interceptam a execução da aplicação de maneira global, como logs de requisições e autenticação. Facilita a aplicação de lógicas globais a todas as requisições
**4. Modules:** Contém todos os módulos da aplicação, onde cada módulo pode ter sua própria implementação específica. Isola as funcionalidades em unidades independentes,
**5. Static:** Armazena arquivos estáticos, como HTML, CSS, JS, ou diretórios para uploads de documentos PDF, vídeos e imagens. Fornece um local centralizado para recursos estáticos.
**6. Templates:** Contém páginas HTML ou engines de templates utilizadas na interface do usuário. Separa a lógica de apresentação do restante do código.
**7. Database:** Engloba a estrutura da base de dados de cada módulo, incluindo a definição de schemas, tabelas, views, triggers e outros objetos. Pode ser independente do software, localizando-se fora da pasta do projeto.
**8. Application:** Responsável pela execução da aplicação, incluindo a inicialização de todos os controllers, middlewares, engines de templates e arquivos estáticos. Serve como ponto de entrada e orquestração, garantindo a correta inicialização e integração de todos os componentes do sistema.

## Conclusão
Recomenda-se aos desenvolvedores dedicarem tempo ao estudo de arquitetura dentro de um software modular. A prática constante pode auxiliar na identificação dos componentes a partir dos requisitos de software. Entender a interação entre os módulos é crucial, visto que isso facilitará a construção de sistemas complexos.

## Exemplo
Criação de um sistema modular usando a linguagem Golang.
Código fonte: https://github.com/ortizdavid/golang-modular-software.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s51jox7akyx3lq4tj0zq.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82ieth9qu3ga4e31kdz6.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f39jwbuyl8msyid857f8.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2w9ayzqswbdleqoevnx.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/beex3sujatsjus0gmqlj.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu2fkmqbfm2hd5d4kx6q.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jg3bwpt3esoxlv31tent.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4vl2wvhh1bmy9ac32b38.png)