<h2>Compreendendo a Camada Model: Fundamentos</h2>
<p>A camada Model é o coração de qualquer aplicação bem arquitetada. Ela representa os dados da sua aplicação e as regras de negócio associadas a eles. Quando falamos em repositórios e mapeamento de dados, estamos trabalhando com dois conceitos complementares que separam a lógica de negócio do acesso aos dados. O Model não deve conhecer como os dados são persistidos; essa responsabilidade fica com o repositório. Já o mapeamento transforma dados brutos do banco em objetos estruturados que sua aplicação compreende.</p>
<p>Pense em um e-commerce: a classe <code>Produto</code> (seu Model) contém informações como nome, preço e descrição. Mas como esses dados chegam do banco de dados? Por meio do repositório, que implementa a interface entre a aplicação e a persistência. Esse padrão garante que sua aplicação seja flexível: você pode trocar de banco de dados sem mexer na lógica de negócio.</p>
<h2>Repositórios: O Padrão que Organiza o Acesso a Dados</h2>
<h3>O que é um Repositório?</h3>
<p>Um repositório é uma abstração que encapsula a lógica de acesso aos dados. Em vez de sua aplicação conversar diretamente com o banco, ela conversa com o repositório, que por sua vez gerencia todas as operações (criar, ler, atualizar, deletar). Isso permite que você mude de tecnologia de persistência sem alterar o código que usa esses dados.</p>
<pre><code class="language-python"># Exemplo em Python com SQLAlchemy
from abc import ABC, abstractmethod
from typing import List, Optional
class RepositorioProduto(ABC):
@abstractmethod
def buscar_por_id(self, id: int) -> Optional['Produto']:
pass
@abstractmethod
def listar_todos(self) -> List['Produto']:
pass
@abstractmethod
def salvar(self, produto: 'Produto') -> 'Produto':
pass
@abstractmethod
def deletar(self, id: int) -> bool:
pass</code></pre>
<h3>Implementação Concreta</h3>
<p>Aqui está uma implementação real que trabalha com um banco SQL:</p>
<pre><code class="language-python">from sqlalchemy.orm import Session
from models import Produto
class RepositorioProdutoSQL(RepositorioProduto):
def __init__(self, session: Session):
self.session = session
def buscar_por_id(self, id: int) -> Optional[Produto]:
return self.session.query(Produto).filter(Produto.id == id).first()
def listar_todos(self) -> List[Produto]:
return self.session.query(Produto).all()
def salvar(self, produto: Produto) -> Produto:
self.session.add(produto)
self.session.commit()
return produto
def deletar(self, id: int) -> bool:
produto = self.buscar_por_id(id)
if produto:
self.session.delete(produto)
self.session.commit()
return True
return False</code></pre>
<p>A vantagem é clara: sua camada de serviço não sabe que está usando SQL. Se amanhã precisar usar MongoDB, você cria um novo repositório sem alterar a lógica de negócio.</p>
<h2>Mapeamento de Dados: Do Banco para Objetos</h2>
<h3>O Desafio da Representação</h3>
<p>O mapeamento transforma dados do banco (colunas, tipos primitivos) em objetos estruturados do seu código. Sem um mapeamento claro, você acaba com código confuso que mistura consultas SQL com lógica de negócio. Existem duas abordagens principais: ORM (Object-Relational Mapping) e mapeamento manual.</p>
<h3>Usando ORM (SQLAlchemy)</h3>
<pre><code class="language-python">from sqlalchemy import Column, Integer, String, Float, DateTime
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
Base = declarative_base()
class Produto(Base):
__tablename__ = 'produtos'
id = Column(Integer, primary_key=True)
nome = Column(String(100), nullable=False)
preco = Column(Float, nullable=False)
descricao = Column(String(500))
criado_em = Column(DateTime, default=datetime.utcnow)
def aplicar_desconto(self, percentual: float) -> float:
"""Lógica de negócio: cálculo de desconto"""
return self.preco * (1 - percentual / 100)
def __repr__(self):
return f"<Produto(id={self.id}, nome='{self.nome}', preco={self.preco})>"</code></pre>
<p>A ORM cuida automaticamente do mapeamento entre colunas e atributos. Quando você faz <code>produto.nome = "Novo Nome"</code>, a ORM sabe que precisa atualizar a coluna correspondente.</p>
<h3>Mapeamento Manual com DTOs</h3>
<p>Para casos mais complexos ou APIs REST, é comum usar Data Transfer Objects (DTOs) para separar a representação interna da externa:</p>
<pre><code class="language-python">from dataclasses import dataclass
from datetime import datetime
@dataclass
class ProdutoDTO:
"""Representação para API - não conhece detalhes do banco"""
id: int
nome: str
preco: float
descricao: str
criado_em: datetime
def mapear_para_dto(produto: Produto) -> ProdutoDTO:
"""Transforma Model em DTO"""
return ProdutoDTO(
id=produto.id,
nome=produto.nome,
preco=produto.preco,
descricao=produto.descricao,
criado_em=produto.criado_em
)
def mapear_para_model(dto: ProdutoDTO) -> Produto:
"""Transforma DTO em Model"""
produto = Produto()
produto.id = dto.id
produto.nome = dto.nome
produto.preco = dto.preco
produto.descricao = dto.descricao
return produto</code></pre>
<p>Essa separação é fundamental quando você quer que a API retorne apenas certos campos ou quando precisa validar dados antes de persistir.</p>
<h2>Integrando Tudo: Repositório + Model + Mapeamento</h2>
<h3>Um Exemplo Prático Completo</h3>
<pre><code class="language-python">class Servicoproduto:
"""Camada de serviço que usa repositório e mapeamento"""
def __init__(self, repositorio: RepositorioProduto):
self.repositorio = repositorio
def criar_produto(self, dto: ProdutoDTO) -> ProdutoDTO:
produto = mapear_para_model(dto)
produto_salvo = self.repositorio.salvar(produto)
return mapear_para_dto(produto_salvo)
def obter_produto(self, id: int) -> Optional[ProdutoDTO]:
produto = self.repositorio.buscar_por_id(id)
return mapear_para_dto(produto) if produto else None
def aplicar_desconto_e_salvar(self, id: int, percentual: float) -> ProdutoDTO:
produto = self.repositorio.buscar_por_id(id)
if not produto:
raise ValueError(f"Produto {id} não encontrado")
novo_preco = produto.aplicar_desconto(percentual)
produto.preco = novo_preco
self.repositorio.salvar(produto)
return mapear_para_dto(produto)</code></pre>
<p>Veja como a lógica de negócio fica clara: não há SQL, não há detalhes de banco de dados. A camada de serviço trabalha com abstrações (repositório) e dados estruturados (DTOs e Models).</p>
<h2>Conclusão</h2>
<p>Os três pontos-chave que você deve lembrar sempre: <strong>Primeiro</strong>, o repositório é sua barreira contra o banco de dados — ele encapsula como você recupera e persiste dados, permitindo mudanças futuras sem impacto na lógica. <strong>Segundo</strong>, o mapeamento (seja via ORM ou manual com DTOs) transforma dados brutos em objetos significativos para seu negócio, evitando que strings genéricas inundem seu código. <strong>Terceiro</strong>, separar Model (o quê), Repositório (como buscar) e Serviço (o quê fazer) cria uma arquitetura que escala e que outros desenvolvedores conseguem entender e modificar sem medo.</p>
<p>Domine esses conceitos e sua aplicação terá uma base sólida para crescer.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://docs.sqlalchemy.org/en/20/orm/quickstart.html" target="_blank" rel="noopener noreferrer">SQLAlchemy ORM Tutorial</a></li>
<li><a href="https://martinfowler.com/eaaCatalog/repository.html" target="_blank" rel="noopener noreferrer">Martin Fowler - Repository Pattern</a></li>
<li><a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" target="_blank" rel="noopener noreferrer">Clean Architecture - Uncle Bob</a></li>
<li><a href="https://docs.python.org/3/library/dataclasses.html" target="_blank" rel="noopener noreferrer">Python Dataclasses Documentation</a></li>
<li><a href="https://dataintensive.net/" target="_blank" rel="noopener noreferrer">Designing Data-Intensive Applications - Martin Kleppmann</a></li>
</ul>