Skip to content

005 — Renda do casal (Household + income mensal por member)

A gente quer registrar quem mora junto e quanto cada um ganha por mês. Isso vira a base pra qualquer pergunta de viabilidade (“essa meta cabe?”, “esse orçamento cabe?”, “sobra quanto?”). Por enquanto só o essencial: o household tem nome, uma moeda, uma lista de members, e um income mensal por member. O income total é a soma.

  • household = a unidade de convivência financeira (o casal, eventualmente com dependentes).
  • member = pessoa que faz parte do household. Promovido de VO (em goals/) pra Entity em shared-kernel/ porque agora vários contexts precisam apontar pro mesmo “Gabriel”.
  • income = quanto o member ganha por mês, na moeda do household.

Scenario: Criar household do casal com income de cada um

Section titled “Scenario: Criar household do casal com income de cada um”
  • Given nenhum household cadastrado
  • When a gente cria o household “Casa” em BRL, adiciona Gabriel e esposa, e atribui income de R$ 8.000 pro Gabriel e R$ 6.000 pra esposa
  • Then monthlyIncome() = R$ 14.000
  • And incomeBy(gabriel) = R$ 8.000 e incomeBy(esposa) = R$ 6.000
  • And members() lista os dois
  • Given o household “Casa” com Gabriel em R$ 8.000 e esposa em R$ 6.000
  • When Gabriel recebe reajuste e assignIncome é chamado de novo com R$ 9.000
  • Then monthlyIncome() = R$ 15.000 (não duplica)
  • And incomeBy(gabriel) = R$ 9.000
  • Given o household “Casa” com Gabriel (R$ 9.000) e esposa (R$ 6.000)
  • When a gente adiciona um terceiro member “dependente” sem chamar assignIncome pra ele
  • Then members().length = 3
  • And monthlyIncome() continua R$ 15.000
  • And incomeBy(dependente) = undefined
  • Context novohousehold/ (aggregate root Household, singular porque um casal tem um household principal).
  • Aggregate RootHousehold (nome, currency: string, members: Member[], incomes: Map<MemberId, Money>).
  • Entity migradaMember sai de goals/ (era VO { name }) e vira Entity em shared-kernel/ com id estável (UUID via crypto.randomUUID()). Identidade por id, não por name.
  • Shared Kernel reusadoMoney.
shared-kernel/Member.ts
Member.create({ name: string }): Member // id gerado via crypto.randomUUID()
member.id: string // readonly, estável
member.name: string // readonly
member.equals(other: Member): boolean // por id
// contexts/household/domain/Household.ts
Household.create({ nome: string, currency: string }): Household
household.nome: string
household.currency: string
household.addMember(member: Member): void
household.assignIncome(member: Member, monthly: Money): void // substitui se já existir
household.monthlyIncome(): Money // soma; zero se ninguém tem income
household.incomeBy(member: Member): Money | undefined // undefined se member sem income
household.members(): Member[]
  • Member promove de VO → Entity porque ID estável vira essencial: persistência (SQLite planejado), cross-context (Goal aponta pro mesmo Gabriel que Household aponta), serialização. Identidade passa a ser id, não name — assim “Gabriel” pode mudar de nome (apelido, sobrenome de casamento) sem virar pessoa diferente.
  • Member mora em shared-kernel/, não em household/. Múltiplos contexts precisam (goals hoje, household agora, budget e atribuição de despesa amanhã). Member não é dono do household — é compartilhado.
  • Single currency por Household — KISS. Casa em BRL, casa em EUR, ok. Casal expat com salários em currencies diferentes entra quando aparecer o caso. Hoje seria complexidade sem demanda.
  • Income é só monthly — sem frequency, sem variable income, sem 13º. YAGNI até cenário pedir.
  • assignIncome sobrescreve, não acumula. Reajuste é a operação natural — chamar de novo com valor novo substitui. Histórico de salário não é deste cenário.
  • Métricas derivadas, não armazenadasmonthlyIncome() recalcula a cada chamada. Consistente com Goal.pace, Budget.total, Invoice.total.
  • Cenário 002 (Meta do casal) vai precisar de touchup minor: Member.of("Gabriel") vira Member.create({ name: "Gabriel" }), e o import muda de ../src/contexts/goals/domain pra ../src/shared-kernel. Goal continua usando Member como antes (a API de igualdade muda de “by name” pra “by id”, mas Goal só faz equals, então é transparente desde que os mesmos Member instances sejam reusados nos paidBy). Esse touchup não é deste scenario — fica registrado aqui só pra deixar claro que a migração tem follow-up.
  • Income variável, freelance, múltiplas fontes por member.
  • Frequência ≠ monthly (semanal, anual, 13º).
  • Taxes, income líquido vs bruto.
  • Dependents econômicos com modelo próprio (quem traz vs quem consome).
  • Joint income / split de despesa entre members.
  • Multi-currency dentro do household.
  • Histórico de income (reajustes anteriores).
  • Member como Aggregate Root — continua Entity simples.
  • Repository / persistência — coleção em memória dentro do aggregate.
  • Update do context goals/ pra novo Member (touchup separado).

Implementar context household/ (aggregate Household) + migração de Member pra shared-kernel/ até os 3 scenarios passarem. Depois, touchup no 002 pra usar o novo Member.