Monolito vs. Microsserviços: Como decidir a arquitetura do seu sistema.

Monolito vs. Microsserviços: Como decidir a arquitetura do seu sistema.

Monolito vs. Microsserviços: Como decidir a arquitetura do seu sistema.

Introdução

A escolha da arquitetura de software para um projeto é uma decisão crucial que pode influenciar significativamente a escalabilidade, manutenibilidade e flexibilidade do sistema. No contexto atual, dois modelos de arquitetura têm ganhado destaque: o monolito e os microsserviços. Embora ambos possam ser utilizados para desenvolver sistemas complexos, é essencial entender as diferenças entre eles para tomar a escolha certa.

No cenário atual, onde a demanda por soluções escaláveis e flexíveis é crescente, a decisão sobre qual arquitetura utilizar se tornou uma questão frequente. O desenvolvedor médio precisa estar preparado para enfrentar esse desafio de forma informada. Neste artigo, iremos explorar as características principais do monolito e dos microsserviços, destacando pontos positivos e negativos de cada modelo. Além disso, forneceremos diretrizes práticas para ajudar a tomar a decisão correta sobre qual arquitetura utilizar em um projeto específico.

Ao final desta exploração, você estará equipado com as habilidades necessárias para avaliar as necessidades do seu projeto e escolher a melhor abordagem. Vamos mergulhar nos detalhes dos dois modelos de arquitetura, comparando os benefícios da integração em um único sistema (monolito) com os benefícios da escalabilidade através de sistemas independentes (microsserviços).

O que é e por que importa

Um monolito é um modelo de arquitetura de software em que todas as funcionalidades e recursos compartilham a mesma código base, geralmente hospedado em uma única aplicação ou processo. Isso significa que o sistema é desenvolvido como um todo, com todos os componentes interligados e dependentes uns dos outros.

Por outro lado, microsserviços são pequenas unidades de software independentes, cada uma responsável por uma funcionalidade específica. Essas unidades podem ser escritas em diferentes linguagens de programação e hospedadas em diferentes ambientes, mas são comunicadas entre si através de serviços de rede.

A escolha entre esses dois modelos de arquitetura depende das necessidades do projeto, incluindo a complexidade do sistema, a escalabilidade requerida e o tipo de mudanças que ocorrerão ao longo do tempo. Monolitos são mais adequados para sistemas simples com poucas funcionalidades e onde as alterações no código afetam apenas uma parte do sistema. Já os microsserviços são mais apropriados para sistemas complexos com muitas dependências, que precisam de alta escalabilidade e flexibilidade, pois permitem mudanças isoladas em cada serviço sem afetar o todo.

Os monolitos oferecem benefícios como melhor desempenho, menor overhead de comunicação entre componentes e uma única fonte de verdade para dados. No entanto, podem ser difíceis de depurar e manter, especialmente se forem muito grandes ou complexos.

Os microsserviços, por sua vez, permitem maior escalabilidade, flexibilidade e robustez, pois cada serviço pode ser atualizado e expandido independentemente dos outros. No entanto, exigem mais infraestrutura de suporte, como serviços de rede e gerenciamento de configurações.

A escolha entre esses dois modelos é crucial para o sucesso do projeto, pois afeta diretamente a manutenibilidade, escalabilidade e flexibilidade do sistema.

Como funciona na prática

Em um sistema de microsserviços, cada serviço é uma aplicação independente que fornece uma funcionalidade específica. Aqui estão as etapas principais do funcionamento interno:

  • Registre e descubra serviços: Cada serviço registra-se em um registro central (como um serviço de descoberta de serviços) com informações sobre sua identidade, endereço IP e porta.
  • Comunicação entre serviços: Quando um serviço precisa se comunicar com outro, ele faz uma solicitação ao registro central para obter o endereço do serviço alvo. Em seguida, os serviços se comunicam diretamente entre si usando protocolos como HTTP ou gRPC.
  • Gerenciamento de configurações: Cada serviço tem sua própria configuração e gerencia-a localmente. Para evitar conflitos de configurações, cada serviço usa um mecanismo de gerenciamento de configurações para obter as configurações necessárias a partir do registro central.
  • Mensageria de mensagem: Em vez de comunicar-se diretamente entre si, os serviços podem usar uma mensageria de mensagem como RabbitMQ ou Apache Kafka para passar mensagens um para o outro. Isso ajuda a evitar bloqueios e permite que os serviços trabalhem de forma assíncrona.
  • Monitoramento e log: Cada serviço é monitorado e suas saídas são registradas. Essas informações são coletadas em um sistema de gerenciamento de logs para facilitar o diagnóstico e a resolução de problemas.

Nesse cenário, cada serviço é uma unidade autônoma que pode ser atualizada, escalada ou até mesmo substituída sem afetar as outras unidades do sistema.

Exemplo real

Aqui está um exemplo de como podemos implementar microsserviços usando a linguagem Java e as ferramentas Spring Boot, Eureka e RabbitMQ.

// Serviço de ordem de serviço (OrderService)
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

@RestController
@RequestMapping("/orders")
public class OrderController {
    
    @Autowired
    private OrderService orderService;
    
    @PostMapping
    public ResponseEntity<Order> createOrder(@RequestBody Order order) {
        return ResponseEntity.ok(orderService.createOrder(order));
    }
}

@Service
public class OrderService {
    
    @Autowired
    private ProductService productService;
    
    public Order createOrder(Order order) {
        // Lógica de negócios para criar uma ordem
        Product product = productService.getProduct(order.getProductId());
        order.setProduct(product);
        return order;
    }
}

// Serviço de produto (ProductService)
@SpringBootApplication
@EnableEurekaClient
public class ProductServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}

@Service
public class ProductService {
    
    @Autowired
    private ProductRepository productRepository;
    
    public Product getProduct(Long id) {
        return productRepository.findById(id).orElseThrow();
    }
}

Nesse exemplo, temos dois microsserviços: OrderService e ProductService. O primeiro é responsável por criar ordens de serviço, enquanto o segundo fornece produtos. A comunicação entre esses serviços é feita através da mensageria RabbitMQ.

// Produtor de mensagens (RabbitMQ)
@Configuration
public class MessagingConfig {
    
    @Bean
    public ConnectionFactory connectionFactory() {
        // Configuração para se conectar ao broker RabbitMQ
    }
}

@Component
public class OrderProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendOrderCreated(Order order) {
        // Envia uma mensagem para o tópico de ordens criadas
        rabbitTemplate.convertAndSend("orders-created", order);
    }
}

Nesse exemplo, quando um pedido é criado no OrderService, ele envia uma mensagem ao tópico orders-created usando RabbitMQ. O ProductService pode então ouvir esse tópico e atualizar seu estado.

// Consumidor de mensagens (RabbitMQ)
@Component
public class ProductConsumer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void receiveOrderCreated(Order order) {
        // Atualiza o produto no banco de dados
        productService.updateProduct(order.getProductId());
    }
}

Essa é uma visão geral simples do como os microsserviços podem se comunicar e compartilhar informações usando mensagens.

Boas práticas

Use a abordagem bottom-up para implementar microsserviços

  • Inicie criando pequenos serviços independentes que executam uma única responsabilidade, como autenticação ou armazenamento de dados.
  • Em seguida, combine esses serviços em um fluxo de trabalho mais amplo.

Implemente a arquitetura em camadas para manter a independência entre microsserviços

  • Desacople os serviços em diferentes camadas da aplicação, como apresentação, lógica de negócios e persistência de dados.
  • Isso permite que cada camada seja modificada sem afetar as outras.

Utilize mensageria para comunicação entre microsserviços

  • Em vez de chamar os serviços direto, use mensagens para que eles possam ser executados assincronamente e sejam mais resistentes a falhas.

Armadilhas comunes

Evite o "monolito" em miniatura ao implementar microsserviços

  • Não crie um conjunto de serviços que são tão complexos quanto o monolito original, apenas distribuídos entre várias máquinas.
  • Em vez disso, procure criar serviços simples e independentes.

Não use mensageria como uma solução para problemas de design

  • A mensageria é uma ferramenta útil para resolver problemas de comunicação entre microsserviços, mas não é uma solução para problemas de design ou arquitetura.
  • Certifique-se de que os serviços estão bem projetados e independentes antes de adicionar mensageria.

Conclusão

Ao decidir entre monolito e microsserviços, é crucial lembrar que a escolha não está em si mesma, mas no modelo de negócios, escalabilidade e complexidade do sistema. A abordagem bottom-up para implementação de microsserviços e a utilização de arquitetura em camadas são ferramentas importantes para manter a independência entre os serviços.

Proximos passos incluem:

  • Desenvolver habilidades em programação concorrente e design de sistemas escaláveis;
  • Aprofundar-se na teoria dos microsserviços e suas melhores práticas, como isolamento de responsabilidades e utilização eficaz da mensageria.
  • Avaliar a necessidade de monitoramento contínuo e melhorias contínuas em um sistema de microsserviços escalável.

Referências

  • Fowler, M. Microservices: a disciplinary problem. Disponível em: https://martinfowler.com/bliki/Microservices.html. Acesso: 2024.
  • Newmann, S. 12 Factor App. Disponível em: https://12factor.net/. Acesso: 2024.
  • Humble, J. e Farley, D. Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation. Addison-Wesley Professional, 2010.
  • Martin, K. N. Microservices Patterns. Manning Publications, 2018.
  • Oreilly, M. Microservices: A Critical Primer. O'Reilly Media, Inc., 2016.
  • Newmann, S. The Microservices Anti-Pattern Catalog. Disponível em: https://microservices-patterns.com/. Acesso: 2024.