Skip to main content

Tech Stack

sdlcs-aws-cdk-lib

sdlcs-aws-cdk-lib is a next-generation accounting and service charge management platform designed specifically for self-managed residential blocks and flat management companies.

SaaS

The app is designed to run for clients in the same way freeagent works, with each building being it's own SaaS, with owners, lease holders and accountants having usernames that work only within there own building, with one account being the owner of the building, seperate to all other users.

🚀 Features

  • Transparent service charge tracking
  • Automatic equal cost sharing
  • Resident-friendly statements
  • Budget vs actual reporting
  • Ledger-based expense management
  • Secure, cloud-based and easy to use

💻 Tech stack

  • NextJS + Tailwind CSS
  • AWS Serverless (Lambda, API Gateway, DynamoDB, S3)
  • Stripe for billing
  • Auth with Clerk
  • PDF reporting
  • Email notifications with AWS SES
  • Unit and integration tests with Jest
  • CDK Typescript for infrastructure as code
  • GitHub Actions for CI/CD
  • Typescript for type safety
  • ESLint and Prettier for code quality
  • Uses npm workspaces for monorepo structure
  • Node v22.x
  • All code is written in TypeScript
  • Uses AWS CDK for infrastructure
  • Always use the latest version of AWS CDK
  • Node version is specified in the .nvmrc file
  • NPM repositories are configured in the .npmrc file
  • Command line tools are defined in the package.json file

Configuration Files

  • ESLint: Follow rules specified in .eslintrc.config.mjs for code quality
  • TypeScript: Adhere to compiler options in tsconfig.json
  • Renovate: Dependency updates managed via renovate.json
  • CDK: Reference cdk.json for CDK app settings and context values
  • Git: Honor exclusions in .gitignore
  • Prettier: Code formatting defined in .prettierrc
  • npm: Package access configured in .npmrc
  • GitHub Actions: Workflows configured in .github/workflows/*.yml
  • When modifying any configuration file, ensure to update documentation accordingly

TypeScript

  • Use strict mode in tsconfig.json
  • Use tsc for type checking
  • Strict Types are defined in the shared/models directory for shared types across frontend and backend
  • Use interface for defining types and public object contracts
  • Use type for defining unions, intersections, and complex types
  • Avoid enum — prefer as const objects with derived union types; use enum only when required for interoperability with external libraries
  • Never use Record<string, any> – use typed objects or Record<string, unknown> with narrowing
  • Use unknown for values that can be of any type but need type checking before use
  • Use any only as a last resort when type safety cannot be guaranteed
  • Use never for functions that never return (e.g., throw an error)
  • Use void for functions that do not return a value
  • Use Promise<T> for asynchronous functions that return a value
  • Use Promise<void> for asynchronous functions that do not return a value
  • Use async/await for asynchronous code
  • Use import statements for importing modules
  • Use export statements for exporting modules
  • Prefer named exports over default exports

Documentation Guidelines

  • Use Markdown format for documentation
  • Use headings, lists, and code blocks for clarity
  • Include diagrams using Mermaid syntax where applicable
  • Use the README.md as the primary documentation entry point
  • Keep documentation in sync with code changes at all times

/docs Directory

  • Place all project documentation in the docs directory
  • Use subdirectories for major sections (e.g., docs/architecture, docs/api, docs/user-guide)
  • Each subdirectory should have an index.md file as the main entry point
  • Use relative links to connect documentation files within the docs directory

README.md Structure

The project README.md should contain:

  • Project overview and purpose
  • Quick start guide with prerequisites
  • Setup instructions (dev environment, dependencies)
  • Architecture diagrams
  • Links to other relevant documentation
  • Troubleshooting section

Code Quality Principles

  • Readability: Easy to read, understand, test, and maintain
  • Operability: Easy to deploy, scale, monitor, and debug
  • Maintainability: Easy to secure, document, refactor, and optimize
  • Extensibility: Easy to extend with new features
  • Imports: Use direct imports (e.g., Duration instead of cdk.Duration)

Workspace Backend

CDK Stacks

  • Auth Stack: Manages user authentication with AWS Cognito and Clerk.
  • API Stack: Sets up the API Gateway, Lambda functions, and DynamoDB tables.
  • Frontend Stack: Deploys the React application to S3 and configures CloudFront for CDN.
  • Email Stack: Configures AWS SES for email notifications.
  • Each CDK Stack must be in its own file
  • File name must match the stack name in PascalCase format
  • Example: MyStack.ts for a stack named MyStack
  • Place stack files in the lib directory (no subdirectories)

CDK Stack Coding Conventions

  • Use cdk-nag to enforce best practices and security checks.
  • Use cdk-assert for unit testing CDK stacks.
  • Never hardcode sensitive information like API keys or secrets in the codebase.
  • Never Use cdk outputs to link resources across stacks. Instead, use ssm parameters.

CDK Constructs

  • Each AWS resource should be defined in its own Construct
  • Place constructs in lib/{resourceType}/ subdirectories
  • File name must match the Construct name in PascalCase format
  • Make all resources public properties for stack access
  • Must follow standard CDK Construct patterns

Lambda Functions

  • Always written in TypeScript
  • Organize in src/lambda/{lambdaName} directories for individual lambdas
  • src/lambda/ must be configured as a separate npm workspace
  • Filename must match the lambda name in camelCase format
  • Each lambda must have:
    • Its own package.json
    • Its own tsconfig.json
    • Its own .gitignore
    • Jest tests in its own test directory
  • Always use the NodeJSFunction construct from AWS CDK
  • Specify runtime as Runtime.NODEJS_22_X
  • API methods for example: ['GET', 'POST', 'PUT', 'DELETE'] should be in a single lambdas

Workspace Frontend

Style Consistency

  • All new product sections must use shared UI components from src/frontend/components/ui where possible.
  • Tailwind CSS configuration is centralized; do not override styles locally unless necessary.
  • Follow the design tokens and UI patterns documented in /docs/frontend/style-guide.md.
  • When adding new UI components, update the style guide and, if using, Storybook stories.
  • Ensure all frontend code is reviewed for visual and interaction consistency with existing sections.

Workspace Shared

Domain Model Definitions

  • All domain models (e.g., Building, LedgerEntry, Unit) are defined in shared/models/.
  • Always import types from shared/models in all backend, frontend, and lambda code.
  • Update or refactor model definitions only in this folder.
  • When adding new models, create a new file in shared/models and export from shared/models/index.ts.
  • All models must include an ownerId field for SaaS multi-tenancy. This is assigned at master account creation and used as the subdomain name.
  • All API, DynamoDB, and business logic must scope data by ownerId.
  • On signup, only master accounts are created, requiring a max 40 character valid word for the subdomain. Sub-users can only be invited by the administrator via the users screen.
  • Use Clerk.com organization feature for member management and invitations.
  • Lambda@Edge must augment all API claims with the ownerId from the subdomain.
  • This ensures consistency, easy refactoring, and version control of all domain types and multi-tenant logic.