Uecko_ERP/docs/architecture/identity-and-companies.md
2026-07-06 14:04:47 +02:00

176 lines
3.2 KiB
Markdown

# Identity And Companies
## Objetivo
Documentar la separación actual entre `modules/identity` y `modules/companies`, evitando mezclar autenticación, memberships y ficha operativa de empresa.
## Responsabilidades por módulo
### `modules/identity`
Gestiona:
- `Account`
- login
- refresh
- logout
- session
- `RefreshToken`
- `CompanyMembership`
- roles/permisos futuros
- validación de acceso `accountId + companyId`
Endpoints activos:
```http
POST /identity/auth/login
POST /identity/auth/refresh
POST /identity/auth/logout
GET /identity/auth/session
```
El access token contiene solo:
```ts
{
accountId: string;
email: string;
}
```
No debe contener:
- `companyId`
- `companySlug`
- roles
- permisos
### `modules/companies`
Es la fuente de verdad de la ficha operativa de empresa.
`Company` funcional vive en `modules/companies`.
Campos actuales de `companies`:
```txt
id
legal_name
trade_name
tin
slug
email
phone
website
status
created_at
updated_at
```
Índices actuales:
```txt
UNIQUE(slug)
INDEX(status)
INDEX(tin)
```
`slug` pertenece a `companies`, no a auth ni al token.
## Relación entre ambos módulos
- `CompanyMembership` vive en `identity`.
- `Company` vive en `companies`.
- No mover memberships a `companies`.
- No evolucionar `identity.Company` como ficha operativa ERP.
## Servicios públicos
### `identity:general`
Expone autenticación y acceso por membership.
Uso conceptual:
```ts
companyAccess.canAccessCompany(...)
companyAccess.findAccessibleCompanyIds(...)
```
`identity` devuelve IDs accesibles por membership, no fichas completas de empresa.
### `companies:general`
Expone consulta de empresas y validación de empresa activa.
Uso conceptual:
```ts
ICompanyPublicServices {
finder: ICompanyPublicFinder;
}
```
## Por qué no existe `GET /identity/companies`
No se documenta `GET /identity/companies` como endpoint válido porque produciría acoplamiento circular:
```txt
identity -> companies -> identity
```
Si `identity` devolviera fichas completas de company, tendría que depender de `companies`.
Como `companies` ya depende de `identity` para autenticación, el diseño correcto es:
- `identity` resuelve membership y accesibilidad por ID
- `companies` resuelve la ficha completa de empresa
El endpoint vigente para el frontend es:
```http
GET /companies/available
Authorization: Bearer <access_token>
```
## Dependencias permitidas
Para módulos tenant-scoped que usan `requireIdentityTenant(params)`, la dependencia explícita debe ser hacia:
- `identity`
- `companies`
Porque el middleware tenant compone:
```txt
identity:general.companyAccess
companies:general.finder
```
## Dependencias prohibidas
No introducir:
- `identity -> companies`
- recomposición manual de internals de `identity`
- `IdentityInternalDeps`
- `getInternal("identity")` desde módulos consumidores
- helpers locales de auth por módulo como patrón estable
## `GET /companies/available`
Semántica actual:
- requiere `Authorization`
- no requiere `X-Company-Id`
- usa `requireIdentityAuthenticated(params)`
- devuelve companies activas accesibles para la cuenta autenticada
La disponibilidad se calcula con:
```txt
membership active en identity
+
company active en companies
```