Software Architecture Lab
Voltar para conceitos
BackendIntermediário12 min

Service Layer

Centralize a lógica de negócio em serviços dedicados. A Service Layer evita que regras espalhadas por controllers e repositories tornem o sistema difícil de manter.

Resumo

A Service Layer (Camada de Serviço) é onde vive a lógica de negócio de uma aplicação. Ela fica entre os controllers (que recebem requisições HTTP) e os repositories (que acessam dados) — e é responsável por orquestrar o que precisa acontecer em cada operação.

Quando a lógica de negócio está no lugar certo, cada camada tem uma responsabilidade clara: o controller cuida de HTTP, o service cuida de negócio, o repository cuida de dados.

Rota de uma Requisição — Service Layer☄ Diagrama de Fluxo
fluxo da requisição
HTTP entra, o service orquestra o negócio, o repository persiste.
request

O problema

Sem uma Service Layer clara, a lógica de negócio vaza para os controllers:

// Controller fazendo trabalho de negócio
async function criarPedido(req: Request, res: Response) {
  const { usuarioId, itens } = req.body;
 
  // validação de negócio no controller
  if (itens.length === 0) {
    return res.status(400).json({ error: "Pedido sem itens" });
  }
 
  // cálculo de negócio no controller
  const total = itens.reduce((s, i) => s + i.preco * i.qtd, 0);
 
  // acesso ao banco no controller
  const pedido = await db.insert("pedidos", { usuarioId, total, itens });
 
  res.json(pedido);
}

Problemas concretos:

  • A lógica de negócio não pode ser reutilizada fora do contexto HTTP
  • Testar exige simular req e res
  • Cada controller que precisa da mesma regra a duplica

Antes e depois

Lógica no controller — não reutilizável
// routes/pedidos.ts
router.post("/pedidos", async (req, res) => {
  const { usuarioId, itens } = req.body;
 
  if (itens.length === 0) {
    return res.status(400).json({ error: "Sem itens" });
  }
 
  const total = itens.reduce((s, i) => s + i.preco * i.qtd, 0);
  const usuario = await db.find("usuarios", usuarioId);
 
  if (usuario.bloqueado) {
    return res.status(403).json({ error: "Usuário bloqueado" });
  }
 
  const pedido = await db.insert("pedidos", { usuarioId, total });
  res.json(pedido);
});
Com Service Layer — lógica isolada e reutilizável
// services/pedido-service.ts
class PedidoService {
  constructor(
    private pedidoRepo: PedidoRepository,
    private usuarioRepo: UsuarioRepository
  ) {}
 
  async criar(usuarioId: string, itens: Item[]) {
    if (itens.length === 0) throw new Error("Pedido sem itens");
 
    const usuario = await this.usuarioRepo.findById(usuarioId);
    if (usuario.bloqueado) throw new Error("Usuário bloqueado");
 
    const total = itens.reduce((s, i) => s + i.preco * i.qtd, 0);
    return this.pedidoRepo.save({ usuarioId, total, itens });
  }
}
 
// routes/pedidos.ts
router.post("/pedidos", async (req, res) => {
  const pedido = await pedidoService.criar(
    req.body.usuarioId,
    req.body.itens
  );
  res.json(pedido);
});

Quando usar

Use a Service Layer quando:

  • A mesma lógica de negócio precisa ser chamada de múltiplos pontos (HTTP, fila, cron job)
  • Os controllers estão crescendo com lógica que não é de HTTP
  • Você quer testar a lógica de negócio sem depender de framework web ou banco
  • Precisa orquestrar múltiplas operações em uma única transação de negócio

Erros comuns

Anemic Service (serviço anêmico):

// Inútil: não há lógica aqui
class UsuarioService {
  async buscar(id: string) {
    return this.repo.findById(id); // só delega
  }
}

Service que acessa HTTP: O service não deve conhecer Request, Response ou códigos de status HTTP. Essas responsabilidades pertencem ao controller.

Dependências hardcoded:

// Errado: impossível de testar ou trocar
class PedidoService {
  private repo = new PedidoRepositoryPostgres(); // acoplado
}
 
// Correto: injetar a dependência
class PedidoService {
  constructor(private repo: PedidoRepository) {}
}

Conceitos relacionados

  • Repository Pattern — fornece acesso a dados que o Service orquestra
  • Injeção de Dependência — mecanismo pelo qual os repositories chegam ao Service sem acoplamento direto
  • Separação de Responsabilidades — o princípio que define por que controller, service e repository têm fronteiras distintas

Conteúdos relacionados