Data contracts, Supabase implementations, request containers, and Result-based domain flows.
Domain logic follows a three-layer pattern that decouples apps from infrastructure providers.
bounded contract packages -> interfaces, schemas, errors, read models
packages/supabase-* -> Supabase-backed implementations
packages/core + core-* -> request context and domain-scoped factoriesThe source/JIT packages foundation, identity, tenant, crm, catalog, work, people, commerce, conversations, automation, platform, and portal define TypeScript interfaces, Zod schemas, and domain error types. They may depend only on lower contract packages and infrastructure-neutral libraries.
Use one shared schema per domain when practical instead of creating duplicate DTOs. Prefer purpose-built list/picker/board/summary/detail read models when callers materially differ.
packages/supabase-*packages/supabase-runtime owns clients, generated database types, and shared provider primitives. The aligned bounded packages (supabase-identity, supabase-tenant, and so on) implement contract interfaces without importing other providers. Core injects cross-domain dependencies and does not expose Supabase details to apps.
packages/core and packages/core-*Create a typed RequestContext with createRequestContext({ bearerToken? }). Cookie and bearer paths create context only; they must not instantiate every domain service.
Each feature or route imports a direct factory such as createTasksService(context) from @workspace/core-work/tasks. When a use case spans domains, compose the two or three explicit factories in that orchestration module. Do not hide them behind string keys, proxies, or an all-services container.
The domain-scoped packages are core-tenant, core-crm, core-catalog, core-work, core-people, core-commerce, core-conversations, core-automation, core-platform, and core-portal. Three more core-* packages hold cross-domain runtimes rather than one domain's factories:
@workspace/core-ai-runtime runs assistant chat and specialist streams for the AI routes in apps/api/app/api/v1/ai/, including the AI approval gate and the agent chat workflows.@workspace/core-workflow-runtime holds the workflow dispatcher (dispatchWorkflowEvent), the scheduler, lifecycle nodes, and the commerce and order event reactors. See Workflows.@workspace/core-command-center composes the tenant, CRM, platform, portal, and automation factories into command center search and commands.Some packages sit beside the three layers instead of inside one of them:
@workspace/ai and @workspace/inbox carry the core boundary tag. ai owns AI Gateway models, assistant tools, prompts, knowledge retrieval, and memory. inbox owns the Chat SDK channel adapters and inbound channel webhook jobs.@workspace/email, @workspace/stripe, @workspace/seo, @workspace/product-pdf, @workspace/quote-pdf, and the packages under packages/integrations/ carry the integration tag. They wrap one external service or renderer, may import only contracts and config, and are composed by core-* packages or apps.Async service methods return Promise<Result<T, E>> using better-result. Domain exceptions should become typed error results rather than thrown exceptions. Use Result.codec only at action/API/AI wire boundaries.
To replace Supabase, create a new provider package that implements the same bounded contract interfaces, then change the factories wired by the matching @workspace/core-* package.
Last updated on