// Payment aggregate interfacetypePaymentinterface{ID()uuid.UUIDAccount()MoneyAccountCounterparty()CounterpartyAmount()int64Category()PaymentCategoryTransactionID()uuid.UUIDCreatedAt()time.TimeSetCategory(categoryPaymentCategory)Payment// More setter methods (immutable pattern)}// Private struct implementationtypepaymentstruct{iduuid.UUIDaccountMoneyAccountcounterpartyCounterpartyamountint64categoryPaymentCategorytransactionIDuuid.UUID}// Factory method with optionsfuncNew(opts...Option)Payment{p:=&payment{}for_,opt:=rangeopts{opt(p)}returnp}funcWithAccount(accMoneyAccount)Option{returnfunc(p*payment){p.account=acc}}
Service with Transaction Coordination
// PaymentService with balance updatestypePaymentServicestruct{repopayment.Repositorypublishereventbus.EventBusaccountService*MoneyAccountServiceuploadRepoupload.Repository}// Create payment with automatic balance updatefunc(s*PaymentService)Create(ctxcontext.Context,entitypayment.Payment,)(payment.Payment,error){iferr:=composables.CanUser(ctx,permissions.PaymentCreate);err!=nil{returnnil,err}varcreatedEntitypayment.Paymenterr:=composables.InTx(ctx,func(txCtxcontext.Context)error{varerrerror// Create payment (creates transaction internally)createdEntity,err=s.repo.Create(txCtx,entity)iferr!=nil{returnerr}// Update account balance in same transactionreturns.accountService.RecalculateBalance(txCtx,createdEntity.Account().ID(),)})iferr==nil{s.publisher.Publish(payment.NewCreatedEvent(ctx,createdEntity,entity))}returncreatedEntity,err}
Multi-currency Transaction Handling
// Transaction with exchange rate supporttypeTransactioninterface{ID()uuid.UUIDAmount()int64// In origin currencyDestinationAmount()int64// In destination currency (optional)ExchangeRate()*decimal.Decimal// Exchange rate usedTransactionType()TransactionType// income, expense, transfer, exchangeTransactionDate()time.Time// ...}// Service handles multi-currency calculationsfunc(s*TransactionService)CreateExchangeTransaction(ctxcontext.Context,originAccountMoneyAccount,destAccountMoneyAccount,amountint64,exchangeRatefloat64,)(Transaction,error){// Calculate destination amountdestAmount:=decimal.NewFromInt(amount).Mul(decimal.NewFromFloat(exchangeRate)).IntPart()// Create transaction with exchange rate trackingreturns.repo.Create(ctx,Transaction{Type:"exchange",Amount:amount,DestinationAmount:destAmount,ExchangeRate:exchangeRate,})}
Report Generation
// Financial report servicetypeFinancialReportServicestruct{queryRepoquery.FinancialReportsQueryRepositorypublishereventbus.EventBus}// Generate income statement for periodfunc(s*FinancialReportService)GenerateIncomeStatement(ctxcontext.Context,startDate,endDatetime.Time,)(IncomeStatement,error){iferr:=composables.CanUser(ctx,permissions.ReportRead);err!=nil{returnnil,err}// Query transactions for periodtransactions,err:=s.queryRepo.GetTransactionsByPeriod(ctx,startDate,endDate,)iferr!=nil{returnnil,err}// Calculate financial metricsrevenue:=s.sumTransactionsByType(transactions,"income")expenses:=s.sumTransactionsByType(transactions,"expense")netIncome:=revenue-expensesreturnNewIncomeStatement(revenue,expenses,netIncome),nil}
Repository with Tenant Isolation
// Payment repository with multi-tenancyfunc(r*paymentRepository)GetByID(ctxcontext.Context,iduuid.UUID,)(payment.Payment,error){constop="payment.Repository.GetByID"// Get tenant from contexttenantID,err:=composables.UseTenantID(ctx)iferr!=nil{returnnil,serrors.E(op,err)}// Query with tenant isolationrow:=composables.UseTx(ctx,r.db).QueryRow(`SELECT id, tenant_id, account_id, counterparty_id, amount
FROM payments
WHERE id = $1 AND tenant_id = $2`,id,tenantID,)varppayment.Paymentiferr:=scanPayment(row,&p);err!=nil{iferrors.Is(err,sql.ErrNoRows){returnnil,serrors.E(op,serrors.KindNotFound)}returnnil,serrors.E(op,err)}returnp,nil}
Permission Matrix
Payment Permissions
Permission
Description
payments:create:all
Create payments for any account
payments:read:all
View all payments
payments:read:own
View payments for own accounts
payments:update:all
Modify any payment
payments:delete:all
Delete any payment
Expense Permissions
Permission
Description
expenses:create:all
Record any expense
expenses:read:all
View all expenses
expenses:read:own
View own expenses
expenses:update:all
Modify any expense
expenses:delete:all
Delete any expense
Account Permissions
Permission
Description
accounts:create:all
Create accounts
accounts:read:all
View all accounts
accounts:update:all
Modify accounts
accounts:delete:all
Delete accounts
Debt Permissions
Permission
Description
debts:create:all
Record debts
debts:read:all
View all debts
debts:update:all
Modify debts
debts:delete:all
Delete debts
Report Permissions
Permission
Description
reports:read:all
View financial reports
reports:create:all
Generate custom reports
API Contracts
Payment Endpoints
GET /finance/payments # List payments (paginated)
GET /finance/payments/:id # Get payment details
POST /finance/payments # Create payment
PUT /finance/payments/:id # Update payment
DELETE /finance/payments/:id # Delete payment
Expense Endpoints
GET /finance/expenses # List expenses
POST /finance/expenses # Record expense
GET /finance/expenses/:id # Get expense details
PUT /finance/expenses/:id # Update expense
DELETE /finance/expenses/:id # Delete expense
Account Endpoints
GET /finance/accounts # List accounts
GET /finance/accounts/:id # Get account details
POST /finance/accounts # Create account
PUT /finance/accounts/:id # Update account
GET /finance/accounts/:id/transactions # Account transactions
Debt Endpoints
GET /finance/debts # List debts
POST /finance/debts # Create debt
GET /finance/debts/:id # Get debt details
PUT /finance/debts/:id # Update debt
POST /finance/debts/:id/settle # Settle debt
Report Endpoints
GET /finance/reports/income-statement # P&L report
GET /finance/reports/cashflow # Cash flow report
GET /finance/reports/account-statement/:id # Account statement
Error Handling
All services use serrors package:
constopserrors.Op="PaymentService.Create"// Validation errorsifinvalidAmount{returnnil,serrors.E(op,serrors.KindValidation,"amount must be positive")}// Business rule errorsifinsufficientBalance{returnnil,serrors.E(op,"insufficient account balance")}// Not found errorsifpaymentnotfound{returnnil,serrors.E(op,serrors.KindNotFound)}
Multi-tenancy Implementation
Data Isolation
-- All finance tables include tenant_idCREATETABLEpayments(iduuidPRIMARYKEY,tenant_iduuidNOTNULLREFERENCEStenants(id)ONDELETECASCADE,-- ...);-- Queries always filter by tenantSELECT*FROMpaymentsWHEREtenant_id=$1ANDid=$2
Currency Configuration
Currencies are global (not tenant-specific)
Tenant can select which currencies to use
Each account has one base currency
Multi-currency transactions tracked with exchange rates