Antes de abordarmos o conceito de Componentes, vale a pena analisar como um programador iniciante costuma estruturar uma aplicação. Geralmente, a escolha é construir um monólito tradicional, agrupando todas as classes de domínio (como Categoria, Produto, Compra, Venda, Pagamento, etc.) em uma única pasta de Entidades. Da mesma forma, criam-se pacotes globais para Repositórios, Serviços e Controllers.
O grande problema dessa abordagem é que ela gera um forte acoplamento entre as classes e as camadas do sistema. À medida que a aplicação cresce, isso desencadeia uma série de dificuldades:
Manutenção e modelagem complexas: As responsabilidades se misturam. Rapidamente, o código passa a ter diversos trechos onde as classes assumem funções que não deveriam ter, tornando a evolução do sistema confusa.
Escalabilidade ineficiente: Para escalar o backend, é necessário replicar a aplicação inteira e todo o banco de dados. Além de consumir mais recursos, isso traz o risco de duplicidade e inconsistência de dados.
Alto risco para o projeto: Com um processo de desenvolvimento lento e inseguro, os custos de manutenção disparam. No pior dos cenários, a inviabilidade técnica pode causar o cancelamento do projeto, forçando a equipe a reescrever um novo produto do zero (pivotar).
Para evitar esses gargalos, a melhor estratégia é organizar a aplicação dividindo os pacotes em componentes independentes. Mesmo que o sistema continue rodando como um monólito (o chamado "monólito modular"), essa prática traz benefícios imediatos e de longo prazo:
Fronteiras definidas: A manutenção se torna muito mais fácil, pois os limites de cada componente ficam claros e a comunicação entre eles pode ser bem documentada.
Preparação para o crescimento: Se houver a necessidade de quebrar o monólito em projetos menores para melhorar a escalabilidade e reduzir custos, a separação já estará encaminhada.
Migração facilitada: Caso a empresa evolua a ponto de precisar de uma arquitetura baseada em microsserviços, essa transição poderá ser feita de forma natural e a um baixo custo.
Para aprofundar esse conceito, veja a seguir alguns artigos que falam sobre Componentes.
- Interface Fornecida: Mostra os serviços que um módulo oferece. É desenhada como um círculo (ou "pirulito") preso a uma linha saindo do componente. [1, 5, 6]
- Interface Requerida: Mostra os serviços ou recursos que um módulo precisa para funcionar. É desenhada como um semicírculo (ou "soquete"). [1, 5]
- Porta: É um ponto de interação distinto entre o componente e o ambiente externo, permitindo isolar a estrutura interna. É representada por um pequeno quadrado na borda do retângulo do componente. [7, 8, 9]
- Porta com Interface Fornecida/Requerida: A interface (círculo ou semicírculo) é anexada diretamente à porta (o quadrado), indicando que aquela interação específica só ocorre através da porta. Quando as interfaces se conectam, elas formam uma junta do tipo "bola-e-soquete". [7, 8, 10, 11]
(conteúdo gerado com I.A.)
Diferença entre Componentes e Pacotes
Elementos Contidos (Dentro do Pacote)
- Qualquer elemento UML: Classes, interfaces, componentes, casos de uso ou até outros subpacotes.
- Membros visíveis: Elementos marcados com símbolos de visibilidade pública (
+), privada (-) ou protegida (#). [4, 5, 6, 7]
Relacionamentos entre Pacotes (Setas de Conexão)
- Importação de Pacote (
«import»): Permite que um pacote use os elementos públicos de outro pacote sem precisar digitar o caminho completo deles. - Acesso de Pacote (
«access»): Semelhante à importação, mas os elementos importados permanecem privados dentro do pacote que os recebeu, impedindo que outros pacotes os vejam. - Mesclagem de Pacotes (
«merge»): Combina o conteúdo de dois pacotes onde os elementos com o mesmo nome são expandidos e unificados. - Dependência Comum: Uma linha tracejada com seta simples que indica que mudanças em um pacote podem afetar o outro. [8, 9]
Um Exemplo
1. Componente: Catalogo
- Estrutura de Pacotes (Pastas Internas):
com.sistema.catalogo.model(Entidades de Produto, Categoria)com.sistema.catalogo.service(Lógica de busca e atualização de estoque)com.sistema.catalogo.api(Contratos expostos para o exterior)
- Portas:
portaConsulta: Centraliza as buscas do sistema.portaEstoque: Exclusiva para movimentação de mercadorias.
- Interfaces Fornecidas:
IQueryCatalogo(naportaConsulta): Métodos comobuscarProdutoPorId()elistarDisponiveis(). Utilizada por Vendas e Compras.IGestaoEstoque(naportaEstoque): Métodos comobaixarEstoque()ereporEstoque(). Utilizada por Vendas (baixa) e Compras (reposição).
- Interfaces Requeridas:
- Nenhuma (o Catálogo costuma ser uma folha independente nesse cenário).
2. Componente: Vendas
- Estrutura de Pacotes (Pastas Internas):
com.sistema.vendas.checkout(Fluxo de fechamento do pedido)com.sistema.vendas.carrinho(Gestão de itens selecionados)com.sistema.vendas.integration(Classes clientes para chamar outros componentes)
- Portas:
portaIntegracaoCatalogo: Canal de saída para consultar e atualizar estoque.portaIntegracaoPagamento: Canal de saída para processar a cobrança do pedido.
- Interfaces Requeridas:
IQueryCatalogo(naportaIntegracaoCatalogo): Necessária para verificar preços e se o item existe.IGestaoEstoque(naportaIntegracaoCatalogo): Necessária para reservar ou abater o item após a venda.IProcessarPagamento(naportaIntegracaoPagamento): Necessária para enviar o valor do pedido e cobrar o cliente.
- Interfaces Fornecidas:
IStatusPedido(em uma porta própria de visualização): Fornece dados para o cliente acompanhar a venda.
3. Componente: Compras
- Estrutura de Pacotes (Pastas Internas):
com.sistema.compras.cotacao(Gestão de preços com fornecedores)com.sistema.compras.pedido(Pedidos de compra emitidos)com.sistema.compras.gateway(Comunicação com sistemas externos/bancos)
- Portas:
portaAbastecimento: Conecta com o estoque do catálogo.portaFinanceiroCompras: Conecta com o fluxo de saída de dinheiro.
- Interfaces Requeridas:
IQueryCatalogo(naportaAbastecimento): Para saber quais produtos estão abaixo do estoque mínimo.IGestaoEstoque(naportaAbastecimento): Para adicionar os produtos comprados de volta ao inventário físico.IEmitirPagamento(naportaFinanceiroCompras): Necessária para ordenar que o componente de Pagamentos pague um fornecedor.
4. Componente: Pagamentos
- Estrutura de Pacotes (Pastas Internas):
com.sistema.pagamentos.core(Lógica de conciliação, estornos e taxas)com.sistema.pagamentos.gateways(Integrações com Stripe, PayPal, Bancos)
- Portas:
portaRecebimentos: Dedicada a transações de entrada (clientes).portaDesembolsos: Dedicada a transações de saída (fornecedores).
- Interfaces Fornecidas:
IProcessarPagamento(naportaRecebimentos): Métodos comoautorizarCartao()ougerarPix(). Conecta com a requisição de Vendas.IEmitirPagamento(naportaDesembolsos): Métodos comoagendarTed()oupagarBoletoFornecedor(). Conecta com a requisição de Compras.
- Interfaces Requeridas:
- Nenhuma interna do sistema (apenas serviços externos como APIs de bancos/adquirentes, que ficam fora do escopo desses quatro componentes).
Resumo das Conexões (Fio de Ligação)
Vendas (portaIntegracaoCatalogo)=> conecta em =>Catalogo (portaConsulta)viaIQueryCatalogoVendas (portaIntegracaoCatalogo)=> conecta em =>Catalogo (portaEstoque)viaIGestaoEstoqueCompras (portaAbastecimento)=> conecta em =>Catalogo (portaEstoque)viaIGestaoEstoqueVendas (portaIntegracaoPagamento)=> conecta em =>Pagamentos (portaRecebimentos)viaIProcessarPagamentoCompras (portaFinanceiroCompras)=> conecta em =>Pagamentos (portaDesembolsos)viaIEmitirPagamento
Implementação
s namespaces dos pacotes não estão de acordo com o padrão do C# e para um caso real deve ser seguido o padrão da linguagem.
port), utilizamos classes aninhadas ou propriedades que expõem e encapsulam as interfaces correspondentes, isolando o comportamento interno do componente.O que notar no código:
- Namespaces como Pacotes: As divisões de
namespace(ex:com.sistema.vendas.checkout) separam fisicamente as pastas internas de cada componente de arquitetura. - Desacoplamento Completo: Note que o
ComponenteVendasnão conhece a classeCatalogoServiceouMotorFinanceiro. Ele conversa estritamente através das propriedades de porta que expõem apenas interfaces (IQueryCatalogo,IProcessarPagamento), respeitando o encapsulamento rígido exigido pelo diagrama de componentes.
Comentários