Skip to content

Address polimórfico compartilhado #252

@danielhe4rt

Description

@danielhe4rt

Parent

Parte da PRD #250 — Módulo Profile Fase 1.

Contexto

Hoje o endereço de um User vive na tabela user_address dentro do módulo Identity, com FK direta pra users. Essa modelagem é rígida — se amanhã um Event ou Tenant precisar de endereço, seria necessário criar outra tabela.

A decisão (ver ADR-0001) é transformar Address em um model polimórfico compartilhado. Qualquer entidade (User, Event, Tenant, Sponsor) pode ter um endereço via morphOne. O model vive fora de módulo (shared/app) porque é infraestrutura transversal.

O que muda

  • Antes: user_address com user_id FK → acoplado ao User
  • Depois: addresses com addressable_type + addressable_id → desacoplado, reutilizável

A tabela user_address antiga permanece no banco como safety net, mas o model He4rt\Identity\User\Models\Address será removido na issue de remoção de legado (#TBD).

O que construir

1. Migration create_addresses_table

Campos:

  • id — UUID, PK
  • addressable_type — string (morph type)
  • addressable_id — UUID (morph id)
  • country — varchar(4), nullable (ISO code)
  • state — varchar(4), nullable (ISO code)
  • city — varchar, nullable
  • zip_code — varchar, nullable
  • timestamps
  • Index composto em (addressable_type, addressable_id)

2. Model Address

Local: app/Models/Address.php (fora de módulo, é shared).

  • final class, HasUuids, HasFactory
  • Relacionamento: morphTo('addressable')
  • Sem lógica de domínio — é um value object persistido

3. Factory AddressFactory

  • database/factories/AddressFactory.php
  • Defaults: country 'BR', state 'SP', city via faker, zip_code via faker
  • State forUser(User $user) e genérico for(Model $model)

4. Trait HasAddress (opcional)

Se fizer sentido, criar um trait App\Concerns\HasAddress que adiciona morphOne(Address::class, 'addressable') pra qualquer model. Usar no User model substituindo a relação address() existente.

5. Registrar morph map

No AppServiceProvider ou em local adequado: 'address' => Address::class. Também registrar os morphable types se necessário.

Cenários BDD

Cenário: User tem um endereço polimórfico
  Dado que existe um User "danielhe4rt"
  Quando atribuo um Address com country "BR", state "SP", city "São Paulo"
  Então o Address é salvo com addressable_type "user" e addressable_id do User
  E $user->address retorna o Address criado

Cenário: Múltiplas entidades com address
  Dado que existe um User e um Event
  Quando ambos têm um Address associado
  Então cada Address tem seu próprio registro
  E os addressable_type são diferentes ("user" vs "event" ou morph alias)

Cenário: User sem endereço retorna null
  Dado que existe um User sem Address associado
  Quando acesso $user->address
  Então retorna null

Cenário: Deletar User deleta Address via cascade
  Dado que um User tem um Address associado
  Quando o User é deletado
  Então o Address associado também é deletado

Cenário: Factory cria Address válido para User
  Quando crio um Address via factory com forUser($user)
  Então o Address pertence ao User
  E os campos country e state são válidos

Acceptance Criteria

  • Migration cria tabela addresses com campos polimórficos e index
  • Model Address em app/Models/Address.php com morphTo
  • Factory AddressFactory com states pra diferentes entidades
  • User model tem relação address() via morphOne(Address::class, 'addressable')
  • Morph map registrado no service provider
  • Testes cobrem todos os cenários BDD
  • Tabela antiga user_address não é tocada (fica no banco)
  • vendor/bin/pint --dirty --format agent passa sem erros

Blocked by

None — pode começar imediatamente (paralelo ao #251).

Metadata

Metadata

Assignees

No one assigned

    Labels

    profileProfile moduleready-for-agentFully specified, ready for an AFK agent

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions