Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works

Random project

SisVendas

Sistema de Vendas OOP

View on GitHub

Anyone may press Interesting, signed in or not. It helps others find this project.

Member rating

No ratings yet.

Sign in to rate this project.

README

Sistema de Pedido de Vendas

Sistema simples de gestão de Clientes, Produtos e Pedidos de Venda desenvolvido em Delphi com arquitetura MVC, persistência em SQLite via FireDAC, aplicando conceitos de SOLID, Fluent Interface, Template Method e Generics.


Índice

  1. Visão Geral
  2. Stack Técnica
  3. Estrutura de Pastas
  4. Etapa 1 — Conexão e Schema
  5. Etapa 2 — BaseDAO Genérico (CRUD Reutilizável)
  6. Etapa 3 — Entidades com Fluent Interface
  7. Etapa 4 — Controllers
  8. Etapa 5 — Form Padrão (Template Method)
  9. Etapa 6 — Forms de Cadastro Filhos
  10. Etapa 7 — Pedido de Venda (Composição)
  11. Etapa 8 — Utilitários (Validação e Parse)
  12. Padrões de Projeto Aplicados
  13. Princípios SOLID Aplicados
  14. Tutorial de Instalação e Distribuição
  15. Como Executar

Visão Geral

O sistema atende três requisitos funcionais:

Módulo Responsabilidade
Clientes Cadastro com Nome, CPF/CNPJ e Cidade
Produtos Cadastro com Descrição, Preço de Venda e Unidade
Pedido de Venda Seleção de cliente, adição de itens e totalização

O foco da arquitetura é separação de responsabilidades e reaproveitamento de código — não duplicar lógica de CRUD, validação ou montagem de formulários.


Stack Técnica

  • Linguagem: Delphi (Object Pascal)
  • UI: VCL (Visual Component Library)
  • Banco: SQLite
  • Acesso a dados: FireDAC
  • Padrão arquitetural: MVC (Model-View-Controller)
  • Estilo de código: Fluent Interface + Generics + Template Method

Estrutura de Pastas

pascal
/src
????????? /Model
???   ????????? Model.Connection.pas      ??? Conex??o FireDAC + cria????o do schema
???   ????????? Model.BaseDAO.pas         ??? CRUD gen??rico reutiliz??vel
???   ????????? Model.Cliente.pas         ??? Entidade + DAO espec??fico
???   ????????? Model.Produto.pas
???   ????????? Model.ItemPedido.pas
???   ????????? Model.Pedido.pas
????????? /Controller
???   ????????? Controller.Cliente.pas
???   ????????? Controller.Produto.pas
???   ????????? Controller.Pedido.pas
????????? /View
???   ????????? View.Principal.pas
???   ????????? View.Clientes.pas         ??? herda de ufPadrao
???   ????????? View.Produtos.pas         ??? herda de ufPadrao
???   ????????? View.Pedido.Venda.pas
????????? /Padrao
???   ????????? ufPadrao.pas              ??? Form pai (Template Method)
????????? /Util
    ????????? Util.Numero.pas           ??? Parse e formata????o de valores
    ????????? Util.Validador.pas        ??? Valida????o fluent reutiliz??vel

A divisão segue MVC clássico com duas pastas extras:

  • Padrao: form pai herdável (recurso de Visual Form Inheritance do Delphi)
  • Util: código compartilhado entre camadas

Etapa 1 — Conexão e Schema

O que faz

A unit Model.Connection.pas cria e mantém a conexão FireDAC com SQLite, e cria as tabelas automaticamente na primeira execução.

Por que assim

  • Singleton estático (class var FConn): garante uma única conexão pro app inteiro. SQLite trabalha bem com conexão única.
  • Lazy initialization (if not Assigned(FConn)): conexão só é aberta quando o primeiro form chama TConnection.Get. Evita custo de inicialização desnecessário.
  • Schema idempotente (CREATE TABLE IF NOT EXISTS): pode rodar quantas vezes quiser, só cria se não existir. Funciona como migration simplificada.
  • ForeignKeys=True na string de conexão: SQLite vem com FK desligada por padrão (legado histórico). Liga pra valer o ON DELETE CASCADE.

Schema do banco

Tabela Campos principais FKs
clientes id, nome, documento (UNIQUE), cidade —
produtos id, descricao, preco_venda, unidade —
pedidos id, data_emissao, cliente_id, total → clientes
pedido_itens id, pedido_id, produto_id, quantidade, valor_unitario → pedidos (CASCADE), produtos

Decisões importantes

  • id INTEGER PRIMARY KEY AUTOINCREMENT: SQLite gerencia o autoincremento. Mais simples que UUID e suficiente pra escala do projeto.
  • total denormalizado em pedidos: redundância proposital. Evita SUM nos itens em toda listagem. Recalculado a cada gravação.
  • ON DELETE CASCADE nos itens: apagar pedido apaga itens junto. Garante integridade sem código extra.

Etapa 2 — BaseDAO Genérico (CRUD Reutilizável)

O que faz

Model.BaseDAO.pas implementa CRUD genérico usando Generics do Delphi (TBaseDAO<T>). Toda entidade que herdar dela ganha automaticamente: Inserir, Atualizar, Excluir, BuscarPorId, Listar.

Como funciona

A classe TEntidade define um contrato que toda entidade deve cumprir:

delphi
class function NomeTabela: string; virtual; abstract;
class function CamposInsert: string; virtual; abstract;
class function CamposUpdate: string; virtual; abstract;
procedure DoDataset(Q: TFDQuery); virtual; abstract;
procedure ParaParams(Q: TFDQuery); virtual; abstract;

A entidade "diz" qual é sua tabela, seus campos e como serializar/deserializar. O TBaseDAO<T> faz o resto.

Por que essa abordagem

Sem BaseDAO Com BaseDAO
Cada entidade tem seu próprio Inserir, Atualizar, etc. Métodos vêm de graça pela herança
SQL repetido em vários lugares SQL gerado dinamicamente
Adicionar nova entidade = copiar/colar CRUD Adicionar entidade = 3 métodos abstratos
Manutenção dolorosa Manutenção centralizada

Exemplo

delphi
TProdutoDAO = class(TBaseDAO<TProduto>); // uma linha
// Pronto: Inserir, Atualizar, Excluir, BuscarPorId, Listar funcionando

O que não é genérico

Operações específicas da entidade ficam no DAO concreto:

delphi
TClienteDAO = class(TBaseDAO<TCliente>)
public
  class function BuscarPorDocumento(const ADocumento: string): TCliente;
end;

A regra: CRUD genérico fica no pai. Consultas de domínio ficam na filha.


Etapa 3 — Entidades com Fluent Interface

O que faz

Cada entidade (TCliente, TProduto, TPedido) tem métodos fluent que retornam Self, permitindo encadeamento.

Exemplo

delphi
Cliente := TCliente.Create
  .ComId(15)
  .ComNome('João Silva')
  .ComDocumento('12345678909')
  .NaCidade('Sinop');

Por que fluent

  • Legibilidade: o código se lê quase como linguagem natural
  • Construção sem variáveis intermediárias: não precisa criar Cliente, depois setar uma a uma
  • Encadeamento com sub-builders (caso do TPedido.ComItem): permite estrutura aninhada visível

Caso especial — TPedido.ComItem

delphi
Pedido := TPedido.Create
  .ParaCliente(FIdCliente)
  .EmitidoEm(Now)
  .ComItem(produto1Id, 2, 1500.00)
  .ComItem(produto2Id, 1, 350.00);

Cada ComItem cria um TItemPedido internamente, adiciona à lista, e retorna o TPedido pra continuar o encadeamento. Padrão "fluent com sub-construção interna".

Quando NÃO usar fluent

  • Setters de leitura de banco (não escreve, só lê)
  • Métodos com side-effect crítico (gravar no banco, enviar e-mail)
  • Properties triviais (Id que vem do banco)

O fluent foi aplicado só em construção/montagem, não em operações de persistência.


Etapa 4 — Controllers

O que faz

Controllers são a camada de aplicação: validam regras, orquestram DAOs e expõem operações pra View.

Estrutura típica

delphi
TClienteController = class
public
  function Salvar(const ACliente: TCliente): string;
  procedure Excluir(AId: Integer);
  function BuscarPorId(AId: Integer): TCliente;
  function Listar: TObjectList<TCliente>;
end;

Por que Salvar retorna string em vez de Boolean ou exception

  • String vazia = sucesso | String com texto = mensagem de erro
  • Mais fácil de exibir na View (ShowMessage(Erro))
  • Evita exception pra fluxo previsível (cliente com documento duplicado não é "excepcional")

Regra de validação centralizada

O Controller usa o TValidador (Etapa 8) pra acumular erros e retornar o primeiro:

delphi
Result := TValidador.Novo
  .Quando(Trim(ACliente.Nome) = '',       'Nome obrigatório')
  .Quando(Trim(ACliente.Documento) = '',  'Documento obrigatório')
  .Quando(DocumentoDuplicado,             'Documento já cadastrado')
.PrimeiroErro;

Separação Controller vs DAO

Controller DAO
Aplica regras de negócio Executa SQL
Valida antes de salvar Persiste sem questionar
Conhece outras entidades Conhece só sua tabela
Decide o que fazer Faz o que mandam

Etapa 5 — Form Padrão (Template Method)

O que faz

ufPadrao.pas é um form pai abstrato que implementa o fluxo padrão de cadastro: Novo, Editar, Excluir, Salvar, Cancelar. Os forms filhos só implementam o que muda.

A unit fica na pasta /Padrao pra deixar claro que é um recurso compartilhado, não uma View específica.

O padrão por trás

Template Method Pattern (Gang of Four) — combinado com Visual Form Inheritance (recurso nativo do Delphi).

Como funciona

pascal
Usu??rio clica "Salvar"
        ???
TfrmPadrao.btnSalvarClick (gen??rico)
        ???
Chama SalvarRegistro (virtual abstract)
        ???
TViewClientes.SalvarRegistro (espec??fico)

O pai define o algoritmo (validar → salvar → atualizar grid → mudar modo). A filha define detalhes (qual entidade montar, qual controller chamar).

Métodos do contrato

delphi
// FILHA OBRIGADA A IMPLEMENTAR
function SQLListagem: string; virtual; abstract;
procedure CarregarRegistroParaEdicao(AId: Integer); virtual; abstract;
function SalvarRegistro(AId: Integer): string; virtual; abstract;
procedure ExcluirRegistro(AId: Integer); virtual; abstract;
procedure LimparCamposEdicao; virtual; abstract;
function NomeEntidade: string; virtual; abstract;

// HOOKS OPCIONAIS (filha PODE sobrescrever)
procedure ConfigurarGrid; virtual;
function PodeExcluir(AId: Integer): Boolean; virtual;
procedure DepoisDeSalvar; virtual;
procedure AntesDeEntrarEmEdicao; virtual;

Por que esse padrão

  • DRY (Don't Repeat Yourself): fluxo de cadastro existe uma vez só
  • Pressão de contrato: abstract força a filha a implementar
  • Hooks opcionais: filha personaliza sem reescrever tudo
  • Mudança de UI atinge todos os cadastros: trocar layout do form pai propaga pra todos os filhos

FDQuery vs ClientDataSet

Foi escolhido TFDQuery direto (não TClientDataSet) porque:

  • Integração nativa com FireDAC (sem conversões)
  • Filtros via Filter/Filtered ou IndexFieldNames
  • Ordenação por coluna do grid funciona sem código extra
  • O SQLListagem da filha vai direto pra qryLista.SQL.Text

Por que o nome ufPadrao

Convenção comum em projetos Delphi:

  • u = unit
  • f = form
  • Padrao = papel desempenhado

Mantém compatibilidade com padrões clássicos de nomenclatura Delphi sem perder clareza.


Etapa 6 — Forms de Cadastro Filhos

O que faz

View.Clientes e View.Produtos herdam de TfrmPadrao e implementam só o que é específico de cada entidade.

Tamanho da filha

Cada filha tem ~80 linhas de código contra ~250 do pai. Reuso de 75% do código.

Estrutura típica

delphi
TViewClientes = class(TfrmPadrao)
  edtNome: TEdit;
  edtDocumento: TEdit;
  edtCidade: TEdit;
private
  FController: TClienteController;
protected
  function SQLListagem: string; override;
  procedure CarregarRegistroParaEdicao(AId: Integer); override;
  function SalvarRegistro(AId: Integer): string; override;
  // ...
end;

Validações com Fluent Validator

delphi
if not TValidador.Novo
  .Quando(Trim(edtNome.Text) = '',      'Nome obrigatório')      .FocarEm(edtNome)
  .Quando(Trim(edtDocumento.Text) = '', 'Documento obrigatório') .FocarEm(edtDocumento)
  .Quando(Trim(edtCidade.Text) = '',    'Cidade obrigatória')    .FocarEm(edtCidade)
.Valido then Exit;

Sem o validador, seriam 12 linhas com if/ShowMessage/SetFocus/Exit espalhados. Com ele, 3 linhas declarativas.

Forms com duplo uso — cadastro e seleção

Os mesmos forms View.Clientes e View.Produtos funcionam como forms de seleção quando chamados pelo pedido. Uma flag FShowMenu controla o comportamento:

  • FShowMenu = True → modo cadastro completo (com menu)
  • FShowMenu = False → modo seleção (chamado via ShowModal)
delphi
// No pedido de venda:
ViewClientes := TViewClientes.Create(Application);
ViewClientes.FShowMenu := False;  // modo seleção

if ViewClientes.ShowModal = mrOk then
  FIdCliente := ViewClientes.FDQueryid.AsInteger;

Vantagens dessa decisão:

  • Zero forms de busca duplicados — não precisa criar View.BuscaCliente, View.BuscaProduto
  • Consistência visual — mesmo grid, mesmo filtro, mesma ordenação em todo lugar
  • Cadastro inline — usuário pode até cadastrar novo cliente sem sair do pedido
  • Manutenção centralizada — mudou um campo? Mudou em todo lugar

Etapa 7 — Pedido de Venda (Composição)

O que faz

View.Pedido.Venda.pas permite criar um pedido completo: seleciona cliente, adiciona vários itens, calcula total e grava em transação.

Estrutura visual

pascal
+---------------------------------------------+
| Cliente: [____________________] [B]         |
| Produto: [____________________] [B]         |
| Un: [__] Vlr: [______] Qtd: [____] Total:[_]|
|                                       [+]   |
+---------------------------------------------+
| [Grid de itens]                             |
|  ID PROD | DESCRICAO | QTD | VLR UNITARIO   |
+---------------------------------------------+
| Total: R$ 1.500,00      [Gravar] [Cancelar] |
+---------------------------------------------+

Composição em vez de herança

Esse form não herda do ufPadrao porque o fluxo é diferente:

  • Não tem grid de listagem de pedidos no mesmo form
  • Tem cabeçalho + itens (estrutura mestre-detalhe)
  • Não tem Novo/Editar/Excluir de pedido — sempre criação nova
  • Reusa os forms de cadastro (Clientes/Produtos) em modo seleção

Princípio aplicado: "preferir composição sobre herança quando o fluxo diverge".

Seleção de cliente e produto via botão [B]

Os botões [B] ao lado de Cliente e Produto abrem os próprios forms de cadastro em modo seleção:

delphi
procedure TViewPedidoVenda.btnBuscarClienteClick(Sender: TObject);
begin
  ViewClientes := TViewClientes.Create(Application);
  ViewClientes.FShowMenu := False;

  if ViewClientes.ShowModal = mrOk then
  begin
    FIdCliente      := ViewClientes.FDQueryid.AsInteger;
    edtCliente.Text := ViewClientes.FDQuerydescricao.AsString;
  end;
  FreeAndNil(ViewClientes);
end;

FDMemTable pros itens

Os itens ficam num TFDMemTable em memória. Só ao clicar "Gravar" a transação acontece. Vantagens:

  • Usuário pode revisar antes de gravar
  • Cancelar é instantâneo (não toca no banco)
  • Não polui o banco com pedidos abandonados

Gravação transacional

delphi
Pedido := TPedido.Create
  .ParaCliente(FIdCliente)
  .EmitidoEm(Now);

FDQueryPedido.First;
while not FDQueryPedido.Eof do
begin
  Pedido.ComItem(produtoId, qtd, vlr);
  FDQueryPedido.Next;
end;

FPedidoController.Salvar(Pedido); // transação no DAO

O TPedidoDAO.Salvar envolve INSERT do cabeçalho + INSERT dos itens em uma única transação. Se algo falhar no meio, rollback total.


Etapa 8 — Utilitários (Validação e Parse)

Util.Validador.pas — Fluent Validator

Centraliza o padrão "validar → mostrar mensagem → focar controle" em uma interface fluent.

Antes:

delphi
if Trim(edtNome.Text) = '' then
begin
  ShowMessage('Nome obrigatório');
  edtNome.SetFocus;
  Exit;
end;
if Trim(edtCidade.Text) = '' then
begin
  ShowMessage('Cidade obrigatória');
  edtCidade.SetFocus;
  Exit;
end;

Depois:

delphi
if not TValidador.Novo
  .Quando(Trim(edtNome.Text) = '',   'Nome obrigatório')   .FocarEm(edtNome)
  .Quando(Trim(edtCidade.Text) = '', 'Cidade obrigatória') .FocarEm(edtCidade)
.Valido then Exit;

Recursos:

  • .Quando(cond, msg) — adiciona regra
  • .FocarEm(controle) — foca o controle se essa regra falhou
  • .Executar(acao) — executa lambda se essa regra falhou (ex: abrir busca)
  • .Valido — mostra dialog e retorna boolean
  • .PrimeiroErro — retorna string do erro (uso em Controller, sem UI)

A mesma classe serve pra View (com UI) e Controller (sem UI). Polimorfismo bem aplicado.

Util.Numero.pas — Parse robusto

Resolve o problema clássico do StrToCurrDef('1.000,00', 0) retornar 0 por confusão de separadores.

delphi
function ParseValor(const ATexto: string): Currency;
var Limpo: string;
begin
  Limpo := StringReplace(ATexto, '.', '', [rfReplaceAll]);
  Limpo := StringReplace(Limpo, ',', FormatSettings.DecimalSeparator, [rfReplaceAll]);
  Result := StrToCurrDef(Limpo, 0);
end;

Por que existe: o parser do Delphi confunde ponto decimal com ponto de milhar dependendo do locale. A função limpa o texto antes de converter, eliminando ambiguidade.

Regra: todo lugar que lê valor formatado de TEdit usa ParseValor, nunca StrToCurrDef/StrToFloatDef direto.


Padrões de Projeto Aplicados

Padrão Onde Por quê
Template Method ufPadrao Define algoritmo no pai, detalhes na filha
Generics TBaseDAO<T> CRUD reutilizável sem perder tipagem
Fluent Interface Entidades, TValidador API legível e encadeável
Singleton TConnection Uma conexão pro app
DAO (Data Access Object) TClienteDAO, etc. Isola SQL do domínio
MVC Estrutura geral Separação de responsabilidades
Composition over Inheritance View.Pedido.Venda Quando fluxo é diferente do padrão
Visual Form Inheritance Forms herdados Recurso nativo Delphi pra UI hierárquica
Reuse Form as Dialog View.Clientes e View.Produtos Mesmo form serve a cadastro e seleção

Princípios SOLID Aplicados

Princípio Como aparece no projeto
S — Single Responsibility Connection só conecta. BaseDAO só persiste. Controller só orquestra. Validador só valida.
O — Open/Closed Adicionar entidade nova não modifica BaseDAO. Adicionar regra nova não modifica Validador.
L — Liskov Substitution Toda subclasse de TEntidade funciona com TBaseDAO<T>. Todo TfrmPadrao filho funciona com o fluxo do pai.
I — Interface Segregation IValidador tem só 5 métodos coesos. Não há "interface gigante que faz tudo".
D — Dependency Inversion Controllers dependem de DAOs concretos (atalho pragmático). Em versão DDD plena, dependeriam de IRepository.

Tutorial de Instalação e Distribuição

O SisVendas não precisa de instalador separado. Toda a inicialização (criação de pastas, extração da DLL, criação do banco) ocorre automaticamente dentro do próprio executável.

Fluxo da primeira execução

pascal
Usu??rio abre SisVendas.exe
        |
        v
1. Existe C:\SisVendas ?
   |-- N??o -> cria a pasta
   +-- Sim -> continua
        |
        v
2. Existe C:\SisVendas\sqlite3.dll ?
   |-- N??o -> extrai do recurso embutido no exe e grava na pasta
   +-- Sim -> continua
        |
        v
3. Existe C:\SisVendas\pedidos.db ?
   |-- N??o -> FireDAC cria o arquivo SQLite automaticamente
   +-- Sim -> abre o banco existente
        |
        v
4. Executa as migrations (CREATE TABLE IF NOT EXISTS)
   tabelas criadas se n??o existirem, ignoradas se j?? existirem
        |
        v
5. Sistema pronto para uso

Como funciona o embutimento da DLL no exe

1. Geração do arquivo de recurso

Antes de compilar o projeto, é necessário executar uma vez:

bash
brcc32.exe sqlite3res.rc

O sqlite3res.rc contém:

pascal
SQLITE3DLL RCDATA "DLL\sqlite3.dll"

O brcc32 lê esse arquivo e converte a sqlite3.dll em um bloco de bytes binários, gerando o sqlite3res.res.

2. Embutimento do recurso no exe

A diretiva no código:

delphi
{$R sqlite3res.res}

Instrui o compilador Delphi a incluir o conteúdo do .res dentro do próprio .exe. O resultado é um executável único e autocontido — sem dependências externas.

3. Extração em tempo de execução

Quando o programa roda pela primeira vez, este trecho é executado:

delphi
RS := TResourceStream.Create(HInstance, 'SQLITE3DLL', RT_RCDATA);
RS.SaveToFile('C:\SisVendas\sqlite3.dll');

O TResourceStream lê o bloco de bytes embutido no exe e o grava como arquivo em disco. A partir daí, o FireDAC consegue carregar o driver SQLite normalmente.

Estrutura de arquivos no ambiente de desenvolvimento

pascal
MeuProjeto\
????????? DLL\
???   ????????? sqlite3.dll           ??? DLL original (fonte para o recurso)
????????? sqlite3res.rc             ??? declara????o do recurso (voc?? cria)
????????? sqlite3res.res            ??? gerado pelo brcc32 (n??o commitar)
????????? SisVendas.dproj
????????? Model.Connection.pas
????????? Win32\
    ????????? Debug\
        ????????? SisVendas.exe     ??? exe j?? cont??m a DLL embutida

O .res é gerado a partir do .rc mais a DLL. Não precisa ir pro repositório Git — basta rodar o brcc32 uma vez ao clonar o projeto.

Estrutura de arquivos no computador do cliente

pascal
C:\SisVendas\
????????? sqlite3.dll    ??? extra??da automaticamente na 1a execu????o
????????? pedidos.db     ??? criado automaticamente na 1a execu????o

O cliente recebe apenas o SisVendas.exe. Não precisa instalar nada.

Pré-requisitos para desenvolvimento

Requisito Onde obter
sqlite3.dll (32 ou 64 bits conforme a plataforma alvo) sqlite.org/download.html → Precompiled Binaries for Windows
brcc32.exe Já incluso no RAD Studio / Delphi — sem instalação adicional

Passo a passo para novos desenvolvedores

  1. Clonar o repositório
  2. Baixar a sqlite3.dll compatível com a plataforma (Win32 ou Win64)
  3. Colocar a DLL em <raiz do projeto>\DLL\sqlite3.dll
  4. Abrir o Prompt do RAD Studio (Ferramentas → Prompt de Comando do RAD Studio)
  5. Navegar até a raiz do projeto:
    bash
    cd C:\caminho\do\projeto
  6. Compilar o recurso:
    bash
    brcc32.exe sqlite3res.rc
  7. Abrir o projeto no Delphi e compilar normalmente (F9)
  8. Executar — a pasta C:\SisVendas e o banco serão criados automaticamente

Observações importantes

Primeira execução requer permissão de escrita em C:\

Em Windows 10/11 com UAC ativo, gravar em C:\ pode exigir que o exe seja executado como Administrador apenas na primeira vez (pra criar a pasta C:\SisVendas e extrair a DLL).

Nas execuções seguintes, o programa já encontra a pasta e os arquivos prontos e não precisa de elevação.

A DLL só é extraída uma vez

O código verifica se sqlite3.dll já existe antes de extrair:

delphi
if not TFile.Exists(DllDestino) then
  ExtrairDLL(DllDestino);

Substituir o exe por uma versão nova não sobrescreve a DLL existente no cliente. Se precisar atualizar a DLL, delete o arquivo em C:\SisVendas\ manualmente ou adicione lógica de versionamento.

As migrations são idempotentes

Todas as tabelas usam CREATE TABLE IF NOT EXISTS. Isso significa que rodar uma versão nova do exe nunca destrói dados existentes — só cria o que ainda não existe.


Como Executar

Pré-requisitos

  • Delphi 10.3 Rio ou superior (com suporte a Generics e FireDAC)
  • Driver SQLite (vem com FireDAC)

Passos

  1. Abrir SisVendas.dproj no Delphi
  2. Build (Shift+F9)
  3. Run (F9)

Primeira execução

  • A pasta C:\SisVendas é criada automaticamente
  • A sqlite3.dll é extraída do exe pra essa pasta
  • O arquivo pedidos.db é criado automaticamente
  • As tabelas são criadas via CREATE TABLE IF NOT EXISTS
  • Não há dados de exemplo — comece cadastrando cliente e produto antes de fazer pedido

Considerações Finais

Este projeto prioriza clareza arquitetural e reuso de código sobre completude funcional. Decisões importantes:

  • In-memory + SQLite: foco no fluxo de dados, não na infra
  • MVC clássico: padrão pedido na proposta, mais simples que DDD completo
  • Fluent everywhere: aprendizado e legibilidade, não modismo
  • Generics no BaseDAO: mostra domínio do recurso, evita código repetido
  • Template Method no ufPadrao: reuso pesado de UI sem perder flexibilidade
  • Form duplo-uso (cadastro + seleção): economia de código sem perder clareza
  • Distribuição self-contained: usuário final recebe um único .exe

A estrutura permite evoluir naturalmente para:

  • Persistência em outro banco (trocar Model.Connection e DAOs)
  • Camada de domínio mais rica (adicionar Value Objects e Aggregate Roots)
  • Testes unitários (Controllers já são testáveis isoladamente)
  • Relatórios e regras fiscais (extensão dos Controllers)

Desenvolvido como avaliação técnica — Delphi / SOLID / Clean Code