Padrões de Domain-Driven Design para este projeto. Use quando: criar novos domínios, estruturar features, decidir onde colocar lógica de negócio, definir responsabilidades de camadas, mapear bounded contexts. Carregue ao iniciar uma feature nova ou ao ter dúvida sobre organização de código.
O projeto usa DDD pragmático — sem CQRS nem Event Sourcing. Foco em separação de responsabilidades clara.
┌─────────────────────────────────────────────┐
│ Interface Layer │ Controllers, Form Requests, Resources
│ (app/Http/) │ Recebe HTTP, devolve Inertia/Redirect
├─────────────────────────────────────────────┤
│ Application Layer │ Services, Jobs
│ (app/Services/) │ Orquestra casos de uso
├─────────────────────────────────────────────┤
│ Domain Layer │ Models, Events, Value Objects, Enums
│ (app/Models/) │ Regras de negócio puras
├─────────────────────────────────────────────┤
│ Infrastructure Layer │ Repositories, Jobs de fila, APIs externas
│ (app/Repositories/, app/Jobs/) │ Acesso a dados e sistemas externos
└─────────────────────────────────────────────┘
HTTP Request
│
▼
[Controller] ← valida via Form Request, chama Service
│
▼
[Service] ← orquestra: chama Repository, dispara Events
│
▼
[Repository] ← acessa banco via Eloquent
│
▼
[Model] ← define estrutura e relacionamentos
│
▼
HTTP Response ← Inertia::render() via API Resource
Responsabilidade: Autenticação, workspaces e memberships.
app/
├── Models/
│ ├── User.php
│ ├── Workspace.php
│ └── WorkspaceUser.php
├── Services/
│ └── WorkspaceService.php
└── Http/
├── Controllers/
│ ├── Auth/
│ └── WorkspaceController.php
└── Resources/
└── WorkspaceResource.php
Regras:
Responsabilidade: Contas bancárias e saldos.
app/
├── Models/Account.php
├── Services/AccountService.php
├── Repositories/
│ ├── Contracts/AccountRepositoryInterface.php
│ └── Eloquent/EloquentAccountRepository.php
└── Http/
├── Controllers/AccountController.php
├── Requests/
│ ├── StoreAccountRequest.php
│ └── UpdateAccountRequest.php
└── Resources/AccountResource.php
Regras:
balance) é armazenado em centavosAccountService::incrementBalance() e decrementBalance() são os únicos pontos de modificação de saldoResponsabilidade: Movimentações financeiras.
app/
├── Models/Transaction.php
├── Services/TransactionService.php
├── Events/
│ ├── TransactionCreated.php
│ └── TransactionDeleted.php
├── Listeners/
│ └── UpdateAccountBalanceOnTransaction.php
└── Http/
├── Controllers/TransactionController.php
└── Resources/TransactionResource.php
Regras de tipo:
debit → debita conta imediatamente (via evento)credit_card → não afeta conta, fica na faturatransfer → gera DUAS transações (débito + crédito)Regras de status:
completed → transação efetivadapending → agendada (não afeta saldo ainda)Responsabilidade: Cartões e faturas.
app/
├── Models/
│ ├── CreditCard.php
│ └── CreditCardInvoice.php
├── Services/
│ ├── CreditCardService.php
│ └── InvoiceService.php
└── Http/
├── Controllers/
│ ├── CreditCardController.php
│ └── CreditCardInvoiceController.php
└── Resources/
├── CreditCardResource.php
└── CreditCardInvoiceResource.php
Regras:
credit_card pertence a uma CreditCardInvoiceclosing_day do cartãotransaction de débito na conta escolhidaResponsabilidade: Categorias de transações.
app/
├── Models/Category.php
├── Services/CategoryService.php
└── Http/
├── Controllers/CategoryController.php
└── Resources/CategoryResource.php
Regras:
workspace_id = null → categoria global (padrão do sistema)workspace_id = X → categoria do workspaceparent_id → hierarquia (ex: "Alimentação > Restaurante")┌──────────────────────────────────────────────────────┐
│ Use EVENTO quando: │
│ - Efeito colateral pode falhar sem afetar operação │
│ - Múltiplos listeners precisam reagir │
│ - Efeito é assíncrono (via queue) │
│ │
│ Use CHAMADA DIRETA quando: │
│ - Efeito é obrigatório para consistência │
│ - Resultado é necessário para continuar │
└──────────────────────────────────────────────────────┘
Exemplo:
┌──────────────────────────────────────────────────────┐
│ Use JOB (queue) quando: │
│ - Operação demora mais de 500ms │
│ - Depende de API externa (AI, email, etc.) │
│ - Pode ser retentada sem efeito colateral duplo │
│ │
│ Use SÍNCRONO quando: │
│ - Operação é rápida e crítica │
│ - Resultado deve ser apresentado na mesma request │
└──────────────────────────────────────────────────────┘
Toda query de dados financeiros DEVE ser filtrada por workspace_id. Não há exceção.
// SEMPRE assim:
Transaction::where('workspace_id', $workspaceId)->...
// NUNCA:
Transaction::all()
Transaction::find($id)
formatCurrency(cents)"150,00" → 15000 antes de enviaroccurred_at é date, não datetime — transações não têm hora de ocorrência relevantecreated_at / updated_at são timestamp — registro de auditoriaDados financeiros nunca são deletados fisicamente — sempre soft delete. Isso garante auditoria e integridade referencial.
Não pule etapas. Cada contexto depende do anterior.