Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/LMendoza70/SSA/llms.txt

Use this file to discover all available pages before exploring further.

The SSA Health Platform is designed to last. The Jurisdicción Sanitaria de Huejutla de Reyes needs a system that can serve its communities reliably for a decade or more — surviving team changes, technology shifts, and evolving public health requirements. Every architectural decision made here is weighed against that ten-year horizon. Flexibility, readability, and correctness are not aspirations; they are hard constraints. The architecture is not the most clever solution possible — it is the most maintainable solution possible, built on widely accepted patterns that any competent developer can reason about without needing a guide to the guide.

Clean Architecture Layers

The platform is structured around four concentric layers. The fundamental law governing all of them is the Dependency Rule: source code dependencies can only point inward. Nothing in an inner layer knows anything about an outer layer.
1

Domain

The innermost layer. Contains entities, value objects, and pure business rules. This layer has zero external dependencies — no frameworks, no database drivers, no HTTP libraries. It models the real-world concepts of the Jurisdicción Sanitaria: Content, MediaAsset, TimelineEvent, Channel. If this layer compiles in isolation, the core business logic is sound.
2

Application

Orchestrates the domain. Contains use cases, application services, and Data Transfer Objects (DTOs). This layer defines what the system can do — publish content, retrieve a timeline, generate a chatbot response — without caring how it is stored or delivered. It depends only on the Domain layer.
3

Infrastructure

Implements the interfaces defined by the Application layer. Contains Prisma repositories, file system and cloud storage adapters (StorageProvider), social network adapters (SocialPublisher), pgvector embedding stores, and LLM integrations. Infrastructure knows about databases and external APIs; the inner layers do not.
4

Presentation

The outermost layer. Contains NestJS controllers on the backend and React components on the frontend. This is a deliberately thin layer — its only job is to translate HTTP requests into application service calls, and application responses into HTTP responses or rendered UI. No business logic lives here.
The dependency rule expressed as a diagram:
Presentation  →  Application  →  Domain
Infrastructure  →  Application  →  Domain
(never the reverse)
Violating the Dependency Rule — for example, importing a Prisma model directly into a domain entity, or referencing an HTTP request object inside an application service — is a hard architectural error, not a style preference. These violations must be caught in code review and corrected before merging.

Modular Monolith

The platform is deployed as a single unit — one NestJS application on the backend, one React application on the frontend. There are no microservices in the first version. This is a deliberate choice: premature distribution adds operational complexity before the domain is well understood. Within that single deployable, however, the codebase is divided into independent, self-contained modules. Each module owns its own controllers, services, repositories, entities, DTOs, validators, and tests. Modules communicate exclusively through their public service interfaces — never through direct imports into another module’s internal directories. This boundary discipline means that if the platform grows to a scale where a module (for example, the Chatbot module) needs to run as a separate service, the extraction is a deployment change, not a rewrite. The business logic does not move; only the transport layer changes.
The rule is simple: if module A needs something from module B, module B must expose a public service method for it. Module A calls that method. Module A never imports from module-b/repositories/ or module-b/entities/ directly.

DDD Lite Approach

Domain Driven Design in its full form introduces aggregates, domain events, sagas, event sourcing, and a significant ceremony around bounded contexts. For a platform of this size and team, that ceremony adds friction without proportional benefit. The SSA platform uses DDD Lite: the valuable parts of DDD without the overhead.

Ubiquitous Language

Every concept in the codebase uses the same term as the business: Content, Publishing, Channel, MediaAsset. There is no translation layer between what the domain experts say and what the code says.

Bounded Contexts

Each module is a bounded context. The word “content” means something specific in the CMS module. The same word in the Social module means “the artifact to be published.” Boundaries prevent semantic drift.

Domain Modeling First

No database schema is designed, no API endpoint is written, and no component is built until the domain is fully modeled. The domain model is the source of truth for everything downstream.
What DDD Lite deliberately omits: aggregate roots as a formal pattern, domain events bus, saga orchestration, event sourcing, and CQRS in the infrastructure layer. These patterns remain available as future evolutions if the domain complexity justifies them.

SOLID Application

The five SOLID principles are applied consistently across every module:
Each module has one reason to change. The Auth module changes when authentication rules change. The CMS module changes when content management rules change. The Chatbot module changes when the RAG strategy changes. No module mixes concerns from multiple domains.Within modules, the same rule applies at the class level: controllers handle HTTP, services handle business logic, repositories handle data access. None of these classes bleeds into another’s responsibility.
The platform is open for extension and closed for modification. Adding a new social network adapter (e.g., ThreadsAdapter) requires creating a new class that implements SocialPublisher — not editing the existing adapters or the Social module’s core service. Adding a new content type requires creating a new entity that extends the Content base — not modifying the existing content types.
All repositories implement interfaces. LocalStorageProvider and a future S3StorageProvider both implement StorageProvider. FacebookAdapter and InstagramAdapter both implement SocialPublisher. Any implementation can be substituted for another without the calling code changing or breaking. This is enforced structurally through TypeScript interfaces.
Interfaces are narrow and specific to the consumer that uses them. A module that needs to read a file does not receive an interface that also exposes methods for deleting buckets. The SocialPublisher interface exposes only what is needed to publish and schedule — nothing else. Large, general-purpose interfaces are avoided.
High-level modules (Application) do not depend on low-level modules (Infrastructure). Both depend on abstractions (interfaces). The Application layer defines the ContentRepository interface; the Infrastructure layer provides the PrismaContentRepository implementation. NestJS dependency injection wires the concrete implementation to the interface at runtime, keeping the Application layer clean.

Quality Priority Order

When trade-offs arise during implementation or review, the following priority order is authoritative:
1

Security

Every endpoint authenticates, authorizes, validates, and sanitizes. No exception. A public health platform handles sensitive institutional data and cannot afford breaches.
2

Correctness

The system must do what it says it does. A publish operation that silently fails is worse than one that surfaces an error. Tests exist to enforce correctness.
3

Maintainability

Code is read far more often than it is written. Readability, consistent conventions, and documented decisions reduce the cost of future changes.
4

Scalability

The modular monolith architecture enables horizontal scaling per module when needed. Database queries are indexed and paginated. But scalability is designed in structurally — not chased prematurely.
5

Performance

SQL queries are optimized, images are loaded lazily, responses are paginated, and caching is applied where appropriate. Performance is a feature, not an afterthought — but it is addressed after correctness is established.
6

Developer Experience

Clear conventions, good tooling, and comprehensive documentation reduce onboarding time and cognitive load. This matters for a platform expected to outlive its initial team.
Performance optimizations should never be introduced at the cost of correctness or maintainability. A faster-but-wrong query is a bug. A faster-but-unreadable function is technical debt. Optimize only after measuring, and only within the bounds of the higher-priority properties.

Build docs developers (and LLMs) love