Decisões arquiteturais antes de gerar código
O Lino gera muito código rapidamente, mas a qualidade do resultado depende das decisões tomadas antes da geração. Escolher limites, tipo de projeto, estratégia de comunicação e nível aceitável de dívida técnica evita que produtividade inicial se transforme em retrabalho.
Use esta página como checklist de arquitetura para alinhar equipe, domínio e operação antes de criar serviços, módulos e recursos avançados.
Tipo de projeto
Antes de rodar lino project new, defina o objetivo do sistema. Um projeto administrativo interno, um SaaS multi-tenant, uma API pública e uma plataforma distribuída têm necessidades diferentes.
- Aplicação simples: poucos contextos, deploy único e baixa necessidade de independência operacional.
- Monólito modular: vários módulos no mesmo processo, com fronteiras explícitas e custo operacional menor.
- Microsserviços: serviços separados, deploy independente, comunicação assíncrona e maior complexidade operacional.
- SaaS: exige isolamento de tenant, segurança forte, secrets, rate limiting, observabilidade e decisões claras de cobrança/operação.
Essa decisão também define quais preocupações devem nascer no primeiro dia. Um SaaS não pode tratar tenant como detalhe tardio; uma API pública não pode nascer sem rate limiting e contratos claros; uma solução com eventos precisa pensar em Outbox, idempotência e observabilidade; uma aplicação com múltiplas culturas deve evitar strings espalhadas desde o início.
Ao criar a fundação, registre as escolhas principais: culturas suportadas, cache distribuído, comunicação assíncrona, analisadores de código, tipo de banco, presença de web app, autenticação, tenant e worker. Essas opções afetam pacotes, templates, AppHost, parâmetros, secrets, projetos gerados e o esforço necessário para alterar a arquitetura depois.
Monólito modular vs. microsserviços
O monólito modular permite organizar o domínio em módulos com contratos internos claros, mantendo build, debug e deploy mais simples. Microsserviços só compensam quando há motivos fortes: escala independente, times separados, fronteiras de negócio estáveis ou requisitos operacionais distintos.
Começar com monólito modular não significa ignorar arquitetura. Significa preservar um deploy simples enquanto o domínio ainda está sendo descoberto, mas já criar limites internos que impedem que tudo vire uma única massa de entidades, serviços e tabelas compartilhadas. Essa é a diferença entre “começar simples” e “começar sem estrutura”.
| Critério | Monólito modular | Microsserviços |
|---|---|---|
| Deploy | Mais simples, normalmente único | Independente por serviço |
| Consistência | Mais fácil manter transações locais | Exige eventos, Outbox e consistência eventual |
| Operação | Menor custo de infraestrutura | Mais observabilidade, filas, retries e automação |
| Refatoração | Mais barata no início | Mais difícil quando contratos já foram publicados |
O Lino suporta os dois caminhos. A decisão deve seguir o domínio e a capacidade operacional da equipe, não apenas a preferência técnica. Microsserviços exigem automação de deploy, observabilidade distribuída, controle de versões de contratos, tolerância a falhas de rede, mensageria, reprocessamento e governança de dados. Se a equipe ainda não consegue operar isso com segurança, distribuir cedo demais costuma aumentar risco.
Use monólito modular quando a prioridade é aprender o domínio, validar produto e manter a operação controlável. Use microsserviços quando a independência operacional já é uma necessidade concreta: escala separada, times com ownership real, ciclo de release independente ou requisitos de infraestrutura incompatíveis entre partes do sistema.
Limites de serviços e módulos
Um limite bom reduz acoplamento e deixa claro quem é dono de cada regra. Evite criar serviços por tabela ou por tela. Serviços e módulos devem representar capacidades de negócio.
- Catalog: produtos, categorias, informações comerciais e dados consultados por vendas.
- Sales: pedidos, carrinho, pricing aplicado, snapshots e fluxo comercial.
- Stock: disponibilidade, reservas e movimentações.
- Security: usuários, roles, permissões, autenticação e tokens.
Um bom limite possui linguagem própria, regras que mudam juntas, dados sob ownership claro e contratos explícitos para conversar com outros contextos. Um limite ruim geralmente aparece como módulo “Common”, entidade compartilhada por várias áreas, tabela consultada por todo mundo ou serviço criado apenas porque havia uma tela nova.
Quando um módulo precisa de dados de outro, escolha entre API, evento de integração, shadow entity ou remodelagem do limite. Acesso direto ao banco de outro módulo é sinal de acoplamento estrutural. Mesmo quando dois módulos rodam no mesmo processo e usam o mesmo banco físico, o schema, os projetos de persistência e os contratos devem proteger o limite de negócio.
- Sinal de bom limite: o módulo consegue evoluir suas entidades e migrations sem obrigar outro módulo a recompilar por detalhe interno.
- Sinal de limite fraco: uma alteração simples em uma entidade exige revisar telas, queries e handlers de módulos que não deveriam conhecer aquela entidade.
- Sinal de dependência escondida: o consumidor precisa “saber demais” sobre tabelas, campos técnicos ou regras internas do produtor.
Dívida técnica consciente
Dívida técnica não é qualquer imperfeição. É uma decisão de adiar qualidade sabendo o custo futuro. O problema é criar dívida sem nome, sem dono e sem plano de pagamento.
Em um SaaS ou sistema modular, a dívida técnica mais perigosa não é apenas um método longo. É a dívida que limita evolução: módulos que não conseguem mudar separadamente, dados de tenant sem isolamento confiável, secrets dentro do repositório, endpoints sensíveis sem limite, eventos sem reprocessamento ou integrações sem contrato claro.
- Documente atalhos tomados durante geração e customização.
- Rode build e testes antes de avançar para a próxima etapa de geração.
- Evite corrigir problemas gerados com hacks locais repetidos; ajuste a modelagem ou o template quando o padrão estiver errado.
Trate cada decisão arquitetural como algo auditável. Se você escolher não habilitar mensageria, registre que os fluxos atuais não dependem de processamento assíncrono. Se escolher coluna por tenant, garanta filtros e testes. Se optar por integração síncrona, assuma timeout, indisponibilidade e erro de contrato como parte do desenho.
Estratégias de comunicação
Escolha a comunicação conforme a necessidade real de consistência, latência, rastreabilidade e acoplamento. A pergunta principal não é “qual tecnologia usar?”, mas “o produtor precisa da resposta agora, a falha do consumidor deve cancelar a operação e quem é dono do dado?”.
Em sistemas modulares, o erro mais comum é transformar comunicação em dependência escondida: um módulo consulta tabela de outro, reaproveita entidade interna, injeta repositório externo ou cria uma chamada síncrona sem tratar falha. Isso parece produtivo no começo, mas torna cada mudança posterior mais arriscada, porque a fronteira deixa de ser explícita.
- Chamada síncrona: boa para consulta imediata e dependência aceitável. Evite em cascatas longas.
- Evento de domínio: reação interna dentro do mesmo limite de aplicação.
- Evento de integração: comunicação entre módulos, serviços ou sistemas, normalmente com Outbox.
- Shadow Entity: cópia local mínima para consulta e validação sem depender diretamente do produtor.
- Integração HTTP externa: apropriada para serviços de terceiros, com timeout, retry, logs e tratamento de falha.
Se a regra exige atualização imediata, use fluxo síncrono ou revise o limite. Se aceita consistência eventual, prefira eventos para reduzir acoplamento. Quando o consumidor precisa apenas ler um pequeno subconjunto de dados de outro contexto, uma shadow entity pode ser mais segura do que expor a entidade original inteira.
| Pergunta | Direção recomendada |
|---|---|
| A resposta é obrigatória para concluir o caso de uso? | Integração síncrona explícita, com timeout, erro previsível e contrato claro. |
| O produtor deve confirmar a transação mesmo se o consumidor estiver fora? | Evento de integração com Outbox e processamento assíncrono. |
| O consumidor só precisa validar ou consultar dados mínimos? | Shadow Entity alimentada por evento ou integração, com ownership local do modelo de leitura. |
| A mudança acontece dentro do mesmo agregado ou bounded context? | Evento de domínio ou lógica de domínio local, sem publicar contrato externo desnecessário. |
