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 15
- Auto-scales from 0.5 to 128 ACU based on load
- Multi-AZ deployment for high availability
- Automated backups and point-in-time recovery
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 Vercel for zero-configuration deployment
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:
-
Identity & Access Context
- Organizations, users, roles, permissions
- Authentication and authorization
-
Campaign Management Context
- Projects (marketing campaigns)
- Templates (video structures with versioning)
-
Integration Context
- API credentials
- Webhook configurations
- Email delivery settings
-
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: User authentication and management
- Campaign Service: Projects and templates
- Integration Service: API credentials and webhooks
- Analytics Service: Event tracking 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):
- Domain Layer (Tests → Implementation → Coverage >95% → Lint clean)
- Application Layer (Tests → Implementation → Coverage >90% → Lint clean)
- Infrastructure Layer (Tests → Implementation → Coverage >85% → Lint clean)
- API Layer (Tests → Implementation → Coverage >90% → Lint clean)
- 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: Google Workspace SSO
Decision: Use Google OAuth for all authentication (no passwords).
Rationale:
- No password management burden (no hashing, reset flows, breach risk)
- Convenient for business users (most have Google accounts)
- Google 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 15.
Rationale:
- Auto-scaling: Scales compute capacity automatically (0.5-128 ACU)
- Cost-effective: Pay only for capacity used
- High availability: Multi-AZ with automatic 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.9% uptime SLA
- Multi-AZ deployment for redundancy
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 → 128 ACU)
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 15, 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).