@cullet/erp-core
v1.0.3
Published
Core ERP with clean architecture, temporality, policies, and rule sets
Readme
erp-core
Núcleo arquitetural para sistemas ERP e domínios transacionais. Primitives tipadas para domínio, policies, erros e application services com clean architecture pronta para receber adapters.
Para o sumário prompt-friendly veja KIT_CONTEXT.md. Para os contratos comuns a todos os kits veja a PHILOSOPHY.md.
O que entrega
- Domínio —
Entity,ValueObject. - Exceções de domínio —
DomainException,InvariantViolationException,InvalidStateTransitionException,ValidationException,BusinessRuleViolationException,EntityNotFoundException. - Erros de aplicação —
AppErrordiscriminada porcode:ValidationError,NotFoundError,ConflictError,AuthorizationError,IntegrationError. - Result —
Result<T, E>eOutcomepara retorno tipado da aplicação. - Policies —
PolicyCatalog,PolicyDefinition,PolicyResolver,PolicyServicee tipos associados para avaliação declarativa. - Application — portas de observabilidade (
LoggerPort,MetricsPort,TracerPort) emapPolicyEvaluationErrorpara integrar a camada de aplicação. - Exemplos — rulesets de referência em
examples/rulesets/, fora da superfície principal de domínio.
Como começa
Import direto (sempre a versão latest exportada pelo pacote):
import {
Entity,
ValueObject,
PolicyCatalog,
PolicyService,
mapPolicyEvaluationError,
type PolicyDecision,
} from "@cullet/erp-core";Pinado em uma versão (recomendado em produção): fixe a versão npm do pacote no
seu package.json (ex.: "@cullet/erp-core": "1.0.0").
import { PolicyResolver } from "@cullet/erp-core";Full-control (kit copiado para dentro do projeto, livre para editar):
npx cullet fc [email protected]O argumento do fc é o nome do kit no registry (erp-core), não o nome npm com escopo. O comando instala @cullet/erp-core, copia o src/ para ./cullet/[email protected]/ e registra o alias @cullet/erp-core no tsconfig.json.
Composicao sem singletons
Para isolamento por tenant, request ou teste, prefira instancias locais em vez
dos exports globais coreConfig e contextResolverRegistry:
import {
ComputeRegistry,
ContextResolverRegistry,
CoreConfig,
GateEngineRegistry,
PolicyContextBuilder,
Result,
registerNamespacedContextResolversIn,
} from "@cullet/erp-core";
import { GateEngineV1 } from "@cullet/erp-core/policies/engines/v1/gate";
const coreConfig = new CoreConfig({
observability: { reporter },
});
const resolverRegistry = new ContextResolverRegistry();
registerNamespacedContextResolversIn(resolverRegistry, "billing", [
{
path: "student.contractStatus",
resilience: {
timeoutMs: 200,
retry: { maxAttempts: 3, initialDelayMs: 25, maxDelayMs: 100 },
circuitBreaker: { failureThreshold: 5, cooldownMs: 1_000 },
},
async resolve(seed) {
return Result.ok(seed.fields.contractStatus);
},
},
]);
const contextBuilder = new PolicyContextBuilder(resolverRegistry);
const gateEngines = new GateEngineRegistry();
gateEngines.register(new GateEngineV1({ coreConfig }));
const computeRegistry = new ComputeRegistry({ coreConfig });Os singletons continuam disponiveis para apps simples, mas nao sao o caminho recomendado quando ha risco de bleed entre composicoes concorrentes.
Decisões tomadas
- Modelo de erro
mixed: domínio lançaDomainException, aplicação retornaResult<T, AppError>, infra traduz paraResult. Não cruze a fronteira. - Temporalidade interna ao kit: o suporte temporal continua no código do kit, mas a API pública principal não expõe um container
Timeline<T>nem helpers temporais dedicados no barrel raiz. - Policies como dados: catalogadas, resolvidas e avaliadas por
PolicyCatalog,PolicyResolverePolicyService. Não sãoifs espalhados pela aplicação. - Disable first-class para definitions:
PolicyDefinitionaceitaenabled: falsepara desligar uma definicao sem removê-la do repositório; o default continuatrue. - Observabilidade só via portas:
LoggerPort,MetricsPort,TracerPortemcore/application/ports/. Sem dependência runtime depino,winston, OpenTelemetry no kit. - Dependência runtime declarada:
zod(validação tipada). Em modo full-control, instale manualmente — ocullet doctore ocullet fcte avisam.
Como evoluir
- Novos use cases: criar em
core/application/consumindo portas existentes; nunca importar deadapters/. - Novas portas: interface em
core/application/ports/, implementação emadapters/<lib>/, sem vazar tipos da lib externa. - Novas exceções de domínio: derive de
DomainExceptionemcore/exceptions/. - Novos erros de aplicação: derive de
AppErrore adicione ocodediscriminado emcore/errors/. - Mudança incompatível: abra
versions/2.0.0/. Regras emkits/VERSIONING.md. - Antes de publicar:
npm run validate-kitspara garantir aderência à filosofia.
