Skip to main content

Implementation Overview

This section describes the architectural decisions, technology choices, and development practices that guide the Percus platform development.

Philosophy​

Percus is built with these core principles:

  • Quality First: High test coverage, strict linting, code reviews
  • Domain-Driven Design: Business logic drives the architecture
  • Serverless First: Focus on value delivery, not infrastructure management
  • Security by Design: Authentication, encryption, and audit logging from day one
  • Progressive Enhancement: Start simple, add complexity when needed

Technology Stack​

Backend​

Runtime & Language:

  • AWS Lambda with .NET 10 (C#)
  • Serverless architecture for automatic scaling and cost optimization
  • .NET 10 chosen for improved cold start performance (~850ms-1s cold start)

Why AWS Lambda + .NET?

  • Zero infrastructure management
  • Automatic scaling (0 to thousands of concurrent executions)
  • Pay only for actual usage
  • .NET provides strong typing, mature tooling, and excellent performance
  • Native async/await support for scalable I/O operations

Database:

  • Amazon Aurora Serverless v2 with PostgreSQL 17
  • Auto-scales from 0.5 to 16 ACU based on load in production (0.5 to 2 in staging)
  • Single-AZ today. Multi-AZ with automatic failover is on the roadmap and is the change that would raise the availability commitment — see Service levels
  • Automated backups with a 30-day retention window and point-in-time recovery, and deletion protection enabled in production

Why PostgreSQL over MySQL?

  • Superior JSON support (JSONB type with indexing)
  • Built-in full-text search
  • Array and composite types
  • Better support with Entity Framework Core
  • More advanced query features (CTEs, window functions)

ORM:

  • Entity Framework Core
  • Type-safe queries with LINQ
  • Automated migrations for schema evolution
  • Change tracking and unit of work pattern

Storage:

  • Amazon S3 for templates, assets, and generated videos
  • CloudFront CDN for global content delivery
  • Signed URLs for secure private content access

API:

  • API Gateway (REST API)
  • Built-in throttling, monitoring, and caching
  • Integration with Lambda proxy pattern

Frontend​

Framework:

  • Next.js 15 (App Router) with TypeScript
  • Server-side rendering (SSR) for better SEO and performance
  • Static generation (SSG) for cacheable pages
  • Deployed on AWS Amplify Hosting (WEB_COMPUTE platform, so Server Components and route handlers run server-side), provisioned by CDK on a percus.video custom domain with an ACM certificate Amplify validates over DNS

Styling:

  • Tailwind CSS for rapid UI development
  • Custom design system with defined color palette
  • Responsive-first approach

State Management:

  • TanStack Query (React Query) for server state
  • Zustand for client state
  • Automatic caching, refetching, and optimistic updates

Forms & Validation:

  • React Hook Form for performant forms
  • Zod for schema validation
  • Type-safe form data with TypeScript

Architectural Patterns​

Domain-Driven Design (DDD)​

We organize the system around business domains, not technical layers.

Bounded Contexts:

The platform is divided into 4 independent bounded contexts:

  1. Identity & Access Context

    • Organizations, users, roles, permissions
    • Authentication and authorization
  2. Campaign Management Context

    • Projects (marketing campaigns)
    • Templates (video structures with versioning)
  3. Integration Context

    • API credentials
    • Webhook configurations
    • Email delivery settings
  4. Analytics Context

    • Video generation events
    • Engagement tracking (views, completions)

Each context has its own:

  • Domain model (entities, value objects, aggregates)
  • Business rules and invariants
  • Database context (Entity Framework)
  • Lambda function (microservice)

Why DDD?

  • Aligns code with business language
  • Clear boundaries reduce coupling
  • Easier to understand, test, and maintain
  • Team members can work independently on different contexts

Clean Architecture​

We follow the dependency inversion principle with clear layers:

┌─────────────────────────────────┐
│ API Layer (Lambda) │ ← HTTP, JSON, DTOs
├─────────────────────────────────┤
│ Application Layer (Use Cases) │ ← Orchestration, validation
├─────────────────────────────────┤
│ Domain Layer (Entities) │ ← Business logic, invariants
├─────────────────────────────────┤
│ Infrastructure (EF, S3, etc.) │ ← Persistence, external services
└─────────────────────────────────┘

Layer Responsibilities:

Domain Layer:

  • Entities with business rules
  • Value objects for validation
  • Aggregates that enforce invariants
  • Domain events for communication
  • No dependencies on other layers

Application Layer:

  • Use cases (one per user action)
  • Domain services (complex business logic)
  • Authorization checks
  • Transaction coordination

Infrastructure Layer:

  • Entity Framework DbContexts
  • Repositories (data access)
  • S3 client (file storage)
  • External API clients

API Layer:

  • Lambda function handlers
  • Request/response DTOs
  • Input validation
  • Error handling and logging

Benefits:

  • Testable (domain has no dependencies)
  • Flexible (swap infrastructure without touching domain)
  • Maintainable (clear separation of concerns)

Microservices Architecture​

Each bounded context is deployed as an independent Lambda function:

  • Identity Service: users, organizations, roles, invitations, authentication
  • Campaign Service: projects, templates, distribution channels, shares, API credentials
  • Render Data Service: per-viewer personalization data for the hosted model — the one non-relational service, backed by DynamoDB rather than Aurora
  • Analytics Service: event ingest, rollups and metrics

Communication:

  • Synchronous: Direct API calls (when needed)
  • Asynchronous: Domain events (preferred for cross-context)

Why Microservices?

  • Independent deployment and scaling
  • Team autonomy (each team owns a service)
  • Technology flexibility (can use different languages if needed)
  • Fault isolation (one service failure doesn't bring down the whole system)

Development Practices​

Test-Driven Development (TDD)​

We write tests before implementation.

Coverage Requirements:

  • Overall: 90% minimum
  • Domain layer: >95% (business logic is critical)
  • Application layer: >90%
  • Infrastructure layer: >85%
  • API layer: >90%
  • Frontend: >90%

Test Types:

Unit Tests (Fast, Isolated):

  • Domain entities and value objects
  • Use cases with mocked dependencies
  • Pure functions and utilities

Integration Tests (Database, External Services):

  • Repository queries (using Testcontainers for real PostgreSQL)
  • API endpoints (using WebApplicationFactory)
  • End-to-end user flows (using Playwright)

Why High Coverage?

  • Confidence in refactoring
  • Documentation of behavior
  • Early detection of regressions
  • Forces better design (testable code is usually better code)

Domain-First Development​

Implementation Order (Strict):

  1. Domain Layer (Tests → Implementation → Coverage >95% → Lint clean)
  2. Application Layer (Tests → Implementation → Coverage >90% → Lint clean)
  3. Infrastructure Layer (Tests → Implementation → Coverage >85% → Lint clean)
  4. API Layer (Tests → Implementation → Coverage >90% → Lint clean)
  5. Frontend (Tests → Implementation → Coverage >90% → Lint clean)

Rule: Do not move to the next layer until the current layer is 100% complete.

Why Domain-First?

  • Business logic is the most valuable code
  • If domain is wrong, everything else is wrong
  • Testing domain is easiest (no dependencies)
  • Provides solid foundation for other layers

Code Quality Standards​

Linting (Enforced in CI/CD):

  • Backend: dotnet format --verify-no-changes
  • Frontend: ESLint + Prettier
  • No merge if linting fails

Static Analysis:

  • Backend: StyleCop, ReSharper
  • Frontend: TypeScript strict mode
  • Security: Snyk, Dependabot for vulnerability scanning

Code Reviews:

  • All code must be reviewed before merge
  • Automated checks (tests, lint, coverage) must pass
  • At least one approval required

Quality Gates (Block Merge If):

  • ❌ Test coverage drops below 90%
  • ❌ Any test failures
  • ❌ Lint errors
  • ❌ TypeScript errors
  • ❌ Security vulnerabilities (high/critical)

Key Architectural Decisions​

Authentication: federated SSO​

Decision: Use federated single sign-on for all authentication (no passwords). Google OAuth 2.0 on all deployments, and Microsoft Entra ID (multi-tenant) where configured.

Rationale:

  • No password management burden (no hashing, reset flows, breach risk)
  • Convenient for business users (most have a Google or Microsoft work account)
  • The identity provider handles MFA and security audits
  • Free for basic OAuth implementation

Implementation:

  • NextAuth.js on frontend
  • JWT tokens for API authorization
  • User creation on first login

Database: Aurora Serverless PostgreSQL​

Decision: Use Aurora Serverless v2 with PostgreSQL 17.

Rationale:

  • Auto-scaling: Scales compute capacity automatically (0.5-16 ACU in production)
  • Cost-effective: Pay only for capacity used
  • Availability path: Aurora supports Multi-AZ with automatic failover. It is not enabled today — the cluster runs a single instance — so recovery currently means a restore rather than a failover
  • PostgreSQL benefits: Better JSON support, full-text search, advanced features

vs DynamoDB:

  • Relational model fits our domain better
  • Complex queries are simpler (JOINs, GROUP BY)
  • Transactions across multiple tables

vs Self-hosted PostgreSQL:

  • No server management
  • Automatic scaling
  • Built-in backups and HA

ORM: Entity Framework Core​

Decision: Use Entity Framework Core for all database access.

Rationale:

  • Type safety: C# models mapped to database tables
  • Migrations: Version control for database schema
  • LINQ queries: Strongly-typed, compile-time checked
  • Change tracking: Automatic dirty detection and updates

Migration Strategy:

  • Developers create migrations locally: dotnet ef migrations add
  • Migrations stored in Git
  • GitHub Actions runs migrations during deployment
  • Fallback Migration Lambda for manual runs

Why not raw SQL or Dapper?

  • EF provides more productivity (less boilerplate)
  • Migrations are critical for schema evolution
  • Trade-off: Slightly slower, but worth it for maintainability

Deployment: Serverless + IaC​

Decision: Use Terraform for infrastructure as code.

Rationale:

  • Reproducible: Same infrastructure in staging and production
  • Version controlled: Changes tracked in Git
  • Multi-cloud: Can migrate away from AWS if needed
  • Modular: Reusable modules for common patterns

CI/CD:

  • GitHub Actions for all automation
  • Push to develop → Deploy to staging
  • Push to main → Deploy to production
  • Automated tests, linting, coverage checks

Monitoring: CloudWatch + Sentry​

Decision: Use CloudWatch for infrastructure, Sentry for application errors.

CloudWatch:

  • Lambda logs, metrics, alarms
  • API Gateway request logs
  • Database query performance

Sentry:

  • Frontend error tracking
  • Backend exception monitoring
  • Release tracking, source maps

Alerts:

  • Slack/Email when errors spike
  • PagerDuty for critical issues (production down)

Security Architecture​

Defense in Depth​

Layer 1: Edge Protection

  • CloudFront + AWS WAF
  • DDoS protection, rate limiting
  • Geographic restrictions (if needed)

Layer 2: API Gateway

  • Request throttling (100 req/min per user)
  • API key validation
  • Request/response size limits

Layer 3: Application

  • JWT token validation
  • Role-based access control (RBAC)
  • Organization-level data isolation

Layer 4: Data

  • Encryption at rest (AWS KMS)
  • Encryption in transit (TLS 1.3)
  • Database credentials in AWS Secrets Manager

Layer 5: Audit

  • All critical actions logged
  • User activity tracking
  • Audit log retention: 1 year

Compliance Readiness​

GDPR:

  • Data export on request
  • Data deletion on request (soft delete + anonymize)
  • Consent tracking
  • Right to be forgotten

SOC 2:

  • Access controls (RBAC)
  • Audit logging
  • Encryption (rest + transit)
  • Incident response plan

Performance Goals​

API Response Time:

  • Less than 500ms for 95th percentile
  • Less than 1s for 99th percentile

Frontend Page Load:

  • Less than 2 seconds for dashboard
  • Less than 3 seconds for project details

Database Queries:

  • Less than 100ms for simple queries
  • Less than 500ms for complex analytics

Video Generation:

  • Less than 10 seconds for typical template
  • Less than 30 seconds for complex templates

Availability:

  • 99.5% monthly is the committed level today — see Service levels for the measurement method, exclusions and recovery objectives
  • Reaching 99.9% requires Multi-AZ with automatic failover, which is not yet in place: with a single instance, any loss requires a restore, and one such event exhausts a 99.9% budget

Cost Optimization​

Serverless Benefits:

  • Pay only for actual usage (no idle servers)
  • Automatic scaling (no over-provisioning)
  • No operational overhead

Estimated Costs (Monthly):

  • MVP (100 users): ~$200
  • Scale (1,000 users): ~$800
  • Enterprise (10,000 users): ~$3,000

Cost Controls:

  • Reserved concurrency on critical Lambdas
  • Aurora auto-pause when idle (development)
  • S3 lifecycle policies (archive old videos)
  • CloudFront caching (reduce origin requests)

Scalability Strategy​

Horizontal Scaling:

  • Lambda: Automatic, up to account limits
  • Aurora: Add read replicas for read-heavy workloads
  • S3: Unlimited storage

Vertical Scaling:

  • Lambda: Increase memory allocation (also increases CPU)
  • Aurora: Scale up ACU capacity (0.5 → 16 ACU today; the ceiling is a configuration change)

Caching:

  • CloudFront: Cache static assets (templates, images)
  • API Gateway: Cache GET responses (1-5 minutes)
  • Application: Redis/ElastiCache if needed (Phase 2+)

Database Optimization:

  • Indexes on foreign keys and search fields
  • Partitioning for large tables (analytics events)
  • Materialized views for complex analytics

Future Enhancements​

Phase 2 (6-12 months):

  • Real-time notifications (WebSockets)
  • Advanced analytics (A/B testing, heatmaps)
  • Template marketplace
  • Multi-language support (Spanish, Portuguese)

Phase 3 (12-24 months):

  • AI-powered features (auto-generate scripts)
  • Mobile app (iOS/Android)
  • Advanced integrations (Salesforce, HubSpot)
  • White-label option for enterprise clients

Summary​

Key Takeaways:

✅ Serverless Architecture: Lambda + Aurora for automatic scaling and cost optimization

✅ Domain-Driven Design: 4 bounded contexts with clear boundaries

✅ Clean Architecture: Domain → Application → Infrastructure → API layers

✅ Test-First Development: 90% coverage requirement, domain-first approach

✅ Quality Standards: Linting enforced, code reviews required, automated quality gates

✅ Security by Design: OAuth, encryption, RBAC, audit logging from day one

✅ Modern Stack: .NET 10, Next.js 15, PostgreSQL 17, TypeScript

This architecture enables us to:

  • Build quickly (serverless, modern tooling)
  • Scale efficiently (auto-scaling, microservices)
  • Maintain quality (tests, linting, reviews)
  • Deliver value (focus on domain, not infrastructure)

Next: See detailed planning documentation in the percus-backoffice repository (.plan/ folder).