Introdução
À medida que os aplicativos da Web crescem, aumenta também a complexidade no desenvolvimento e na manutenção do sistema. Uma maneira comum de resolver esse problema é usar a arquitetura de microsserviços, onde os desenvolvedores dividem os sistemas em componentes menores e bem gerenciados que podem ser gerenciados e dimensionados individualmente.
Para fazer isso de forma eficaz, geralmente é útil usar uma estrutura de microsserviços. Mas escolher a estrutura certa que ofereça suporte nativo a microsserviços pode ser um desafio. Neste artigo, vamos dar uma olhada em Encore.ts e Nest.js como as duas alternativas relevantes, uma vez que ambas suportam nativamente arquiteturas de microsserviços e TypeScript.
Encore.ts é uma estrutura de código aberto mais recente que se destaca por seus recursos de alto desempenho, segurança de tipo e observabilidade. Nest.js, por outro lado, lidera a estrutura TypeScript para a construção de aplicativos de microsserviços. Cada um deles tem algo forte a oferecer, por isso examinaremos cada estrutura em termos de arquitetura, desempenho e escalabilidade e explicaremos como determinar qual pode funcionar melhor para você.
Antes de começar, vamos dar uma olhada nos dados de benchmark na imagem abaixo:
Os dados de benchmark mostram que o Encore.ts pode lidar com 121.005 solicitações por segundo sem validação e 107.018 com validação de esquema. Isso é significativamente mais rápido que as estruturas tradicionais. Por exemplo, Express.js com Zod atinge apenas cerca de 15.707 solicitações por segundo sem validação e 11.878 com ela. Portanto, o Encore.ts é cerca de 9 vezes mais rápido que o Express, no qual o Nestjs foi desenvolvido.
Visão geral de Encore.ts e NestJS
Ao iniciar um projeto, você deseja uma estrutura que não seja apenas poderosa, mas também fácil de usar pelos desenvolvedores. Encore.ts e NestJS se destacam quando se trata de estruturas de microsserviços que possuem suporte integrado para Typescript, mas funcionam de maneiras distintas.
Encore.ts é uma estrutura nativa da nuvem de código aberto projetada para desenvolvimento de back-end com automação de infraestrutura integrada. Ele permite construir sistemas distribuídos modulares usando bibliotecas de infraestrutura declarativa.
Encore.ts opera em um tempo de execução Rust **** integrado com Node.js via napi para desempenho excepcional no tratamento de E/S e multithreading, permitindo que você escreva lógica em TypeScript.
Aqui está um exemplo simples de como você pode definir um serviço em Encore.ts:
import { Service } from "encore.dev/service";
export default new Service("hello");
Sair do modo de tela cheia
Quando isso hello
serviço é criado, o Encore.ts trata automaticamente o diretório inteiro como parte do serviço – nenhuma configuração extra é necessária.
Por outro lado, NestJS tem seu próprio estilo. É uma estrutura TypeScript flexível que permite controlar totalmente como você constrói seu aplicativo, dando-lhe a liberdade de estruturar as coisas do seu jeito.
Embora não lide com automação de infraestrutura, o NestJS facilita a integração com praticamente qualquer biblioteca de terceiros, o que abre muitas possibilidades para diferentes projetos.
Veja como você pode definir um serviço semelhante no NestJS:
import { Controller, Get } from '@nestjs/common';
@Controller('hello')
export class HelloWorldController {
@Get()
sayHello(): string {
return 'Hello, World!';
}
}
Sair do modo de tela cheia
NestJS oferece mais flexibilidade, mas sem a automação integrada encontrada no Encore.ts.
Arquitetura e Design
A arquitetura de uma estrutura determina como seu aplicativo é construído e mantido ao longo do tempo. Tanto o Encore.ts quanto o NestJS são robustos, mas suas filosofias centrais são diferentes.
Encore.ts é opinativo e *priorizando a nuvem, tornando-o ideal para grandes fontes de segurança *sistemas distribuídos com muitos microsserviços. Um de seus recursos de destaque é o suporte nativo para Pub/Sub, permitindo uma arquitetura orientada a eventos de forma integrada.
Veja como você pode definir um serviço orientado a eventos no Encore.ts usando o Pub/Sub:
import { Topic, Subscription } from "encore.dev/pubsub";
// Define the event type for order creation
export interface OrderCreatedEvent {
orderId: string;
}
// Create a topic for order creation events
export const orders = new Topic<OrderCreatedEvent>("orders", {
deliveryGuarantee: "at-least-once",
});
// Create a subscription to listen for the order creation event
export const _ = new Subscription(orders, "process-order", {
handler: async (event: OrderCreatedEvent) => {
console.log('Order created:', event.orderId);
},
});
Sair do modo de tela cheia
NestJSembora seja capaz de oferecer suporte a microsserviços e arquiteturas orientadas a eventos, oferece uma abordagem mais modular. Seu núcleo segue o MVC padrão e permite que os desenvolvedores construam sistemas à sua maneira, fornecendo maior controle sobre as configurações.
Por exemplo, aqui está como você pode definir serviços e eventos no NestJS com uma abordagem muito mais modularizada:
// order.event.ts
export class OrderCreatedEvent {
constructor(public readonly order: Order) {}
}
// order.repository.ts
@Injectable()
export class OrderRepository {
async save(order: Order): Promise<Order> {
// Persistence logic
}
}
// order.service.ts
@Injectable()
export class OrderService {
constructor(
private readonly orderRepository: OrderRepository,
private readonly eventEmitter: EventEmitter2
) {}
async createOrder(orderDto: CreateOrderDto): Promise<Order> {
const order = new Order(orderDto);
const savedOrder = await this.orderRepository.save(order);
this.eventEmitter.emit(
'order.created',
new OrderCreatedEvent(savedOrder)
);
return savedOrder;
}
}
// order.listener.ts
@Injectable()
export class OrderEventListener {
@OnEvent('order.created')
async handleOrderCreatedEvent(event: OrderCreatedEvent) {
// Handle event logic separately
}
}
// order.module.ts
@Module({
imports: (EventEmitterModule.forRoot()),
providers: (
OrderService,
OrderRepository,
OrderEventListener
),
exports: (OrderService)
})
export class OrderModule {}
Sair do modo de tela cheia
Por design, o NestJS concede muito controle sobre como os componentes irão interagir, mas a desvantagem é muito mais padronizada e você também terá que gerenciar as configurações da infraestrutura por conta própria.
Recursos integrados e extensibilidade
No desenvolvimento de sistemas distribuídos, os recursos fornecidos pela estrutura muitas vezes facilitarão o desenvolvimento, correndo o risco de introduzir complexidade excessiva.
O destaque do Encore.t é que ele fornece maneiras de automatizar o provisionamento de infraestrutura, tanto no desenvolvimento local quanto em ambientes de nuvem. Isso inclui bancos de dados, Pub/Sub, cron jobs e muito mais. Encore.ts também fornece um painel de desenvolvimento local que gera automaticamente documentação de API, diagramas de arquitetura e rastreamento distribuído. Ele também gera clientes front-end, incluindo suporte a especificações OpenAPI para APIs REST, o que pode economizar muito tempo para o desenvolvedor.
Aqui está um exemplo de definição de uma API REST em Encore.ts, que também gera automaticamente a documentação OpenAPI:
import { api } from "encore.dev/api";
interface CreateOrderParams {
productId: string;
quantity: number;
}
interface OrderResponse {
orderId: string;
message: string;
}
export const createOrder = api(
{ method: "POST", path: "/orders", expose: true, auth: true },
async (params: CreateOrderParams): Promise<OrderResponse> => {
const orderId = "order123";
return {
orderId,
message: `Order created for product ${params.productId} with quantity ${params.quantity}.`,
};
}
);
Sair do modo de tela cheia
Com Encore.ts, no momento em que você define seu serviço, a documentação e os diagramas ficam automaticamente disponíveis sem configuração adicional.
NestJS se tornou popular devido à sua flexibilidade. Desde o primeiro dia, ele oferece suporte a REST, GraphQL e WebSocket com facilidade, mas o principal por trás de sua popularidade é que ele se conecta facilmente a bibliotecas de terceiros.
Por exemplo, se você deseja adicionar suporte a GraphQL, é um processo simples.
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
// Define a simple model for an order (this will be your GraphQL type)
class Order {
id: string;
product: string;
quantity: number;
}
// Resolver to handle GraphQL queries and mutations
@Resolver(() => Order)
export class OrderResolver {
// Array to store orders (simulating a database)
private orders: Order() = ();
// Query to get all orders
@Query(() => (Order), { name: 'getAllOrders' })
getAllOrders(): Order() {
return this.orders;
}
// Mutation to create an order
@Mutation(() => Order)
createOrder(
@Args('product') product: string,
@Args('quantity') quantity: number,
): Order {
const newOrder = { id: Date.now().toString(), product, quantity };
this.orders.push(newOrder);
return newOrder;
}
}
Sair do modo de tela cheia
O NestJS simplifica a construção de seus recursos principais, mas não oferece o mesmo nível de infraestrutura e recursos automatizados que o Encore.ts oferece.
Desempenho e escalabilidade
O desempenho é fundamental na construção de sistemas distribuídos, especialmente em escala.
Encore.ts foi desenvolvido para alto desempenho com seu tempo de execução Rust, que lida com operações de E/S e multithreading com eficiência. A velocidade e a segurança da memória do Rust dão ao Encore.ts uma vantagem significativa sobre estruturas puramente baseadas em Node.js. Em termos de escalabilidade, o Encore.ts é nativo da nuvem e pode ser escalonado automaticamente usando arquitetura sem servidor ou Kubernetes, dependendo da sua estratégia de implantação.
O NestJS, por outro lado, é mais tradicional na forma como lida com desempenho e escalabilidade. Como o NestJS é puramente baseado em TypeScript e JavaScript, ele depende das otimizações de desempenho que você aplica durante a configuração. O dimensionamento de um aplicativo NestJS normalmente envolve a configuração manual de Kubernetes, Docker ou plataformas sem servidor, como AWS Lambda.
Embora o NestJS ofereça flexibilidade na forma como você escala, a configuração requer mais esforço manual do que a automação integrada do Encore.ts.
Vamos entender a diferença de desempenho entre encore.ts e Nest.js a partir dos dados de benchmark na imagem abaixo:
A partir dos dados de benchmark, o encore.ts se destaca no que diz respeito ao desempenho, com um tempo de início de apenas 8,3 milissegundos, enquanto o NestJS leva cerca de 143,7 milissegundos, o que o torna quase nove vezes mais rápido que os frameworks tradicionais.
Estratégias de implantação
A forma como você implanta seu aplicativo é uma consideração importante para qualquer projeto, especialmente quando se pensa em ambientes de nuvem.
Encore.ts oferece um caminho fácil para implantação por meio de suas ferramentas de código aberto ou da Encore Cloud Platform. Usando a versão de código aberto, você pode usar encore build
para construir seu projeto e criar uma imagem Docker, que pode então ser implantada em qualquer lugar com suporte para Docker:
encore build docker --services=service1,service2 --gateways=api-gateway MY-IMAGE:TAG
Sair do modo de tela cheia
Isso cria uma imagem Docker que pode ser implantada em qualquer lugar.
Alternativamente, se você optar por usar a Encore Cloud Platform, ela automatiza todo o pipeline de CI/CD, implantando diretamente em sua própria nuvem na AWS ou GCP com opções sem servidor ou Kubernetes.
Por outro lado, NestJS requer configuração manual para implantação. Normalmente, os desenvolvedores usam o Docker para conteinerizar aplicativos NestJS e implantá-los em um provedor de nuvem de sua escolha. Embora isso lhe dê controle sobre sua estratégia de implantação, requer mais configuração – mesmo para um aplicativo simples, você precisa passar por várias etapas:
- Crie um
Dockerfile
:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ("npm", "run", "start:prod")
Sair do modo de tela cheia
- Crie um
docker-compose.yml
arquivo:
version: '3.8'
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=myapp
Sair do modo de tela cheia
- Criar fluxo de trabalho do GitHub Actions para NestJS
name: Deploy NestJS
on:
push:
branches: ( main )
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build and push Docker image
run: |
docker build -t myapp .
docker push myregistry/myapp:latest
- name: Deploy to cloud
run: |
# Add your cloud deployment commands here, e.g., kubectl apply, AWS ECS deploy, etc.
Sair do modo de tela cheia
Quanto maior o seu aplicativo se torna e quanto mais necessidade você tem de vários ambientes de preparação e teste, mais onerosa se torna essa abordagem de configuração manual – crescendo continuamente em termos de tempo gasto em manutenção.
Considerações de caso de uso
Ao escolher entre Encore.ts e NestJS, a decisão deve ser baseada nas necessidades específicas do seu projeto.
Encore.ts é perfeito para aplicativos que priorizam a nuvem e grandes sistemas distribuídos que se beneficiam da automação integrada. Seu tempo de execução e gerenciamento de infraestrutura alimentados por Rust o tornam ideal para arquiteturas orientadas a eventos, microsserviços e aplicativos de alto desempenho. Encore está crescendo rapidamente comunidade é uma fonte confiável de suporte e de encontrar maneiras de integrar ferramentas de terceiros.
Por outro lado, NestJS brilha quando flexibilidade e customização são necessárias. É adequado para aplicativos corporativos que exigem controle refinado sobre todos os aspectos e onde é aceitável gastar tempo na configuração manual. O ecossistema relativamente extenso e o suporte da comunidade do NestJS facilitam a localização de recursos e ferramentas de terceiros.
Conclusão
Escolhendo entre Encore.ts e NestJS se resume às necessidades específicas do seu projeto.
Se você está procurando uma estrutura simples, de alto desempenho, nativa da nuvem e com automação integrada, o Encore.ts é uma excelente escolha. Ele agiliza o desenvolvimento de sistemas distribuídos gerenciando a infraestrutura automaticamente, e seu desempenho baseado em Rust é difícil de superar.
No entanto, se você precisa de uma estrutura modular muito flexível que lhe dê controle sobre cada aspecto minucioso, o NestJS é provavelmente a melhor opção. Sua extensibilidade e grande ecossistema fazem dele uma escolha sólida para soluções empresariais personalizadas.
Ambas as estruturas são poderosas por si só, e a melhor escolha depende se você valoriza desempenho e simplicidade ou total flexibilidade e controle.
Próximas etapas
Se desempenho e simplicidade são importantes para o seu projeto, pode ser uma boa ideia experimentar o Encore.ts. E é tudo Open Source, então você pode conferir o código e contribuir GitHub.