PHP

O que Todo Dev Deve Saber sobre Camada Model: Repositórios e Mapeamento de Dados

8 min de leitura

O que Todo Dev Deve Saber sobre Camada Model: Repositórios e Mapeamento de Dados

Compreendendo a Camada Model: Fundamentos 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. Pense em um e-commerce: a classe (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. Repositórios: O Padrão que Organiza o Acesso a Dados O que é um Repositório? Um

<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) -&gt; Optional[&#039;Produto&#039;]:

pass

@abstractmethod

def listar_todos(self) -&gt; List[&#039;Produto&#039;]:

pass

@abstractmethod

def salvar(self, produto: &#039;Produto&#039;) -&gt; &#039;Produto&#039;:

pass

@abstractmethod

def deletar(self, id: int) -&gt; 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) -&gt; Optional[Produto]:

return self.session.query(Produto).filter(Produto.id == id).first()

def listar_todos(self) -&gt; List[Produto]:

return self.session.query(Produto).all()

def salvar(self, produto: Produto) -&gt; Produto:

self.session.add(produto)

self.session.commit()

return produto

def deletar(self, id: int) -&gt; 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__ = &#039;produtos&#039;

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) -&gt; float:

&quot;&quot;&quot;Lógica de negócio: cálculo de desconto&quot;&quot;&quot;

return self.preco * (1 - percentual / 100)

def __repr__(self):

return f&quot;&lt;Produto(id={self.id}, nome=&#039;{self.nome}&#039;, preco={self.preco})&gt;&quot;</code></pre>

<p>A ORM cuida automaticamente do mapeamento entre colunas e atributos. Quando você faz <code>produto.nome = &quot;Novo Nome&quot;</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:

&quot;&quot;&quot;Representação para API - não conhece detalhes do banco&quot;&quot;&quot;

id: int

nome: str

preco: float

descricao: str

criado_em: datetime

def mapear_para_dto(produto: Produto) -&gt; ProdutoDTO:

&quot;&quot;&quot;Transforma Model em DTO&quot;&quot;&quot;

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) -&gt; Produto:

&quot;&quot;&quot;Transforma DTO em Model&quot;&quot;&quot;

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:

&quot;&quot;&quot;Camada de serviço que usa repositório e mapeamento&quot;&quot;&quot;

def __init__(self, repositorio: RepositorioProduto):

self.repositorio = repositorio

def criar_produto(self, dto: ProdutoDTO) -&gt; ProdutoDTO:

produto = mapear_para_model(dto)

produto_salvo = self.repositorio.salvar(produto)

return mapear_para_dto(produto_salvo)

def obter_produto(self, id: int) -&gt; 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) -&gt; ProdutoDTO:

produto = self.repositorio.buscar_por_id(id)

if not produto:

raise ValueError(f&quot;Produto {id} não encontrado&quot;)

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>

Comentários

Mais em PHP

Segurança em PHP: XSS, CSRF, Injeção e Boas Práticas Finais: Do Básico ao Avançado
Segurança em PHP: XSS, CSRF, Injeção e Boas Práticas Finais: Do Básico ao Avançado

XSS (Cross-Site Scripting) XSS ocorre quando um atacante injeta código JavaSc...

Dominando Herança e Polimorfismo em PHP em Projetos Reais
Dominando Herança e Polimorfismo em PHP em Projetos Reais

Herança em PHP: Fundamentos e Prática Herança é um dos pilares da Programação...

Laravel Testes com PHPUnit e Pest: Do Básico ao Avançado
Laravel Testes com PHPUnit e Pest: Do Básico ao Avançado

PHPUnit: Fundamentos e Configuração PHPUnit é o framework de testes mais cons...