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.mjsfor code quality - TypeScript: Adhere to compiler options in
tsconfig.json - Renovate: Dependency updates managed via
renovate.json - CDK: Reference
cdk.jsonfor 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
tscfor type checking - Strict Types are defined in the
shared/modelsdirectory for shared types across frontend and backend - Use
interfacefor defining types and public object contracts - Use
typefor defining unions, intersections, and complex types - Avoid
enum— preferas constobjects with derived union types; useenumonly when required for interoperability with external libraries - Never use
Record<string, any>– use typed objects orRecord<string, unknown>with narrowing - Use
unknownfor values that can be of any type but need type checking before use - Use
anyonly as a last resort when type safety cannot be guaranteed - Use
neverfor functions that never return (e.g., throw an error) - Use
voidfor 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/awaitfor asynchronous code - Use
importstatements for importing modules - Use
exportstatements 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
docsdirectory - Use subdirectories for major sections (e.g.,
docs/architecture,docs/api,docs/user-guide) - Each subdirectory should have an
index.mdfile as the main entry point - Use relative links to connect documentation files within the
docsdirectory
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.,
Durationinstead ofcdk.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.tsfor a stack namedMyStack - Place stack files in the
libdirectory (no subdirectories)
CDK Stack Coding Conventions
- Use
cdk-nagto enforce best practices and security checks. - Use
cdk-assertfor 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
testdirectory
- Its own
- Always use the
NodeJSFunctionconstruct 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/uiwhere 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/modelsin 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/modelsand export fromshared/models/index.ts. - All models must include an
ownerIdfield 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
ownerIdfrom the subdomain. - This ensures consistency, easy refactoring, and version control of all domain types and multi-tenant logic.