Skip to content

Commit 572881f

Browse files
jeastham1993scottgerringCopilot
authored
feat: add AWS implementation and IaC (#36)
* feat: correct sticker-award to handle errors according to API spec * feat: add AWS implementation and IaC * Update user-management/infra/aws/lib/network.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat: update connection string * chore: add labels to docker image * chore: remove ASPNET Lambda hosting * chore: add todos for future shared infra * feat: enable static analysis (#44) * feat: implement fixes from static analysis * feat: add static analysis configuration * feat: add static analysis configuration * fix: remove duplicate .sln file * Update user-management/src/Stickerlandia.UserManagement.Api/Configurations/RemoveSwaggerDefinitionsFilter.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Merge pull request #45 from DataDog/feat/add-datadog-to-docker feat: add Datadog tracer to Dockerfiles * feat(user-management): update docs (#37) * chore: update OpenAPI docs * chore: update README * chore: add license and notice files * chore: add dockerignore file * Update user-management/src/Stickerlandia.UserManagement.Api/Configurations/RemoveSwaggerDefinitionsFilter.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update user-management/src/Stickerlandia.UserManagement.Api/Configurations/AuthorizeOperationFilter.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update user-management/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore: expand local dev and code structure docs * chore: add cloud provider specific SLN files * fix: specify .sln in dotnet restore * chore: docs updates --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat: add AWS implementation and IaC * feat: update connection string * Update user-management/infra/aws/lib/network.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore: add labels to docker image * chore: remove ASPNET Lambda hosting * chore: add todos for future shared infra * chore: fix static analysis for AWS impls * feat: add arch diagram and README for AWS infra --------- Co-authored-by: Scott Gerring <scott@scottgerring.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com>
1 parent 630ded4 commit 572881f

68 files changed

Lines changed: 5964 additions & 1165 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

user-management/CLAUDE.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Architecture Overview
6+
7+
This is a .NET 8 User Management Service implementing ports and adapters architecture with platform adaptability by design. The service can run on Azure, AWS, or any cloud-agnostic container orchestrator.
8+
9+
### Core Components
10+
- **Core** (`Stickerlandia.UserManagement.Core`) - Domain services and business logic
11+
- **Auth** (`Stickerlandia.UserManagement.Auth`) - Authentication using OpenIddict
12+
- **Agnostic** (`Stickerlandia.UserManagement.Agnostic`) - Cloud-agnostic implementations (Kafka, Postgres)
13+
- **AWS** (`Stickerlandia.UserManagement.AWS`) - AWS-specific implementations (SNS, SQS)
14+
- **Azure** (`Stickerlandia.UserManagement.Azure`) - Azure-specific implementations (Service Bus)
15+
16+
### Driving Adapters (Entry Points)
17+
- **Api** - ASP.NET minimal API (`/api/users/v1`)
18+
- **Worker** - Background worker service
19+
- **FunctionApp** - Azure Functions
20+
- **Lambda** - AWS Lambda functions
21+
22+
## Development Commands
23+
24+
### Local Development with .NET Aspire
25+
Run from `src/Stickerlandia.UserManagement.Aspire/`:
26+
27+
```bash
28+
# Agnostic services (Kafka, Postgres)
29+
dotnet run -lp agnostic
30+
31+
# Azure services (Azure Functions, Service Bus, Postgres)
32+
dotnet run -lp azure_native
33+
34+
# AWS services (Lambda, SNS, SQS, Postgres)
35+
dotnet run -lp aws_native
36+
```
37+
38+
### Testing
39+
40+
**Unit Tests:**
41+
```bash
42+
cd tests/Stickerlandia.UserManagement.UnitTest
43+
dotnet test
44+
```
45+
46+
**Integration Tests (requires environment variables):**
47+
```bash
48+
cd tests/Stickerlandia.UserManagement.IntegrationTest
49+
50+
# Agnostic integration tests
51+
export DRIVING=AGNOSTIC && export DRIVEN=AGNOSTIC && dotnet test
52+
53+
# Azure integration tests
54+
export DRIVING=AZURE && export DRIVEN=AZURE && dotnet test
55+
56+
# AWS integration tests
57+
export DRIVING=AWS && export DRIVEN=AWS && dotnet test
58+
```
59+
60+
### Infrastructure Deployment
61+
62+
**AWS CDK (from `infra/aws/`):**
63+
```bash
64+
npm run build # Compile TypeScript
65+
npm run test # Run infrastructure tests
66+
npm run cdk # CDK commands
67+
```
68+
69+
**Azure Terraform (from `infra/azure/`):**
70+
Standard Terraform commands for Azure resources.
71+
72+
## Code Quality Standards
73+
74+
- **Static Analysis**: Enforced via `Directory.build.props` and `CodeAnalysis.props`
75+
- **Warnings as Errors**: All projects treat warnings as errors
76+
- **Nullable Reference Types**: Enabled across all projects
77+
- **Implicit Usings**: Enabled for cleaner code
78+
79+
## Event Architecture
80+
81+
The service publishes and consumes events via:
82+
- **Outbox Pattern**: Implemented for reliable event publishing
83+
- **Platform-specific Messaging**: SNS/SQS (AWS), Service Bus (Azure), Kafka (Agnostic)
84+
- **Event Schemas**: Documented in `docs/async_api.yaml`
85+
86+
## Key Patterns
87+
88+
- **CQRS**: Commands and queries are separated in the Core domain
89+
- **Dependency Injection**: All adapters registered via service extensions
90+
- **Configuration**: Platform-specific configuration in each adapter
91+
- **Background Processing**: Handled by workers, functions, or lambdas depending on platform

user-management/CodeAnalysis.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
<_SkipUpgradeNetAnalyzersNuGetWarning>false</_SkipUpgradeNetAnalyzersNuGetWarning>
1414
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
1515
<!-- CA2007 is calling .ConfigureAwait(false), which isn't required for non-libraries -->
16-
<NoWarn>$(NoWarn);CA2007</NoWarn>
16+
<NoWarn>$(NoWarn);CA2007;CA1016</NoWarn>
1717
</PropertyGroup>
1818
</Project>

user-management/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@ The project follows a ports and adapters architecture style, split down into `Dr
2323
- **Stickerlandia.UserManagement.Auth** - Core library for auth concerns using [OpenIddict](https://documentation.openiddict.com/)
2424

2525

26+
### AWS Architecture
27+
28+
![AWS architecture diagram](./docs/aws_arch.png)
29+
30+
The AWS native implementation of the user management service uses a combination of containers and functions as a service (FaaS), as well as native messaging services. The different components on this diagram tie to specific classes in the AWS CDK IaC project:
31+
32+
- [API](./infra/aws/lib/api.ts)
33+
- Amazon ECS for container orchestration, with Fargate providing the underlying compute
34+
- A traditional Postgres database, for use with the [OpenIddict](https://documentation.openiddict.com/) auth libraries
35+
- [Background Workers](./infra/aws/lib/background-workers.ts)
36+
- AWS Lambda for the compute, both handling external events and running on a schedule to process items from the outbox
37+
- Rules are defined on a shared external Amazon Event Bridge event bus
38+
- Amazon SQS provides durability at the boundaries of the system
39+
- Internal domian events are published to Amazon SNS
40+
- [Shared](./infra/aws/lib/sharedResources.ts)
41+
- Currently, these are manually created. Eventually these will be created in an external stack and this construct will pull from
42+
2643
## API Endpoints
2744

2845
### User Management API (`/api/users/v1`)

user-management/Stickerlandia.UserManagement.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{FC9DDF76-8
2727
EndProject
2828
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stickerlandia.UserManagement.Agnostic", "src\Stickerlandia.UserManagement.Agnostic\Stickerlandia.UserManagement.Agnostic.csproj", "{295E8315-2CD2-4CD8-B7F4-21EF587B74A9}"
2929
EndProject
30-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stickerlandia.UserManagement.Lambda", "src\Stickerlandia.UserManagement.Lambda\src\Stickerlandia.UserManagement.Lambda\Stickerlandia.UserManagement.Lambda.csproj", "{5C770F92-B6E7-4270-A041-A32F385DA6C4}"
30+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stickerlandia.UserManagement.Lambda", "src\Stickerlandia.UserManagement.Lambda\Stickerlandia.UserManagement.Lambda.csproj", "{5C770F92-B6E7-4270-A041-A32F385DA6C4}"
3131
EndProject
3232
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driving", "Driving", "{924749FC-98D5-40A6-8F4B-BA9BFDEDDEC3}"
3333
EndProject

user-management/docs/aws_arch.png

1.8 MB
Loading
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.js
2+
!jest.config.js
3+
*.d.ts
4+
node_modules
5+
6+
# CDK asset staging directory
7+
.cdk.staging
8+
cdk.out
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.ts
2+
!*.d.ts
3+
4+
# CDK asset staging directory
5+
.cdk.staging
6+
cdk.out

user-management/infra/aws/cdk.json

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"app": "npx ts-node --prefer-ts-exts bin/user-service.ts",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"**/*.d.ts",
11+
"**/*.js",
12+
"tsconfig.json",
13+
"package*.json",
14+
"yarn.lock",
15+
"node_modules",
16+
"test"
17+
]
18+
},
19+
"context": {
20+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
21+
"@aws-cdk/core:checkSecretUsage": true,
22+
"@aws-cdk/core:target-partitions": [
23+
"aws",
24+
"aws-cn"
25+
],
26+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
27+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
28+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
29+
"@aws-cdk/aws-iam:minimizePolicies": true,
30+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
31+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
32+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
33+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
34+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
35+
"@aws-cdk/core:enablePartitionLiterals": true,
36+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
37+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
38+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
39+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
40+
"@aws-cdk/aws-route53-patters:useCertificate": true,
41+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
42+
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
43+
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
44+
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
45+
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
46+
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
47+
"@aws-cdk/aws-redshift:columnId": true,
48+
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
49+
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
50+
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
51+
"@aws-cdk/aws-kms:aliasNameRef": true,
52+
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
53+
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
54+
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
55+
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
56+
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
57+
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
58+
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
59+
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
60+
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
61+
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
62+
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
63+
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
64+
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
65+
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
66+
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
67+
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
68+
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
69+
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
70+
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
71+
"@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false,
72+
"@aws-cdk/aws-ecs:disableEcsImdsBlocking": true,
73+
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
74+
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
75+
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
76+
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
77+
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
78+
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
79+
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
80+
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
81+
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
82+
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true,
83+
"@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true,
84+
"@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true,
85+
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
86+
"@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": false,
87+
"@aws-cdk/aws-s3:setUniqueReplicationRoleName": true,
88+
"@aws-cdk/aws-events:requireEventBusPolicySid": true,
89+
"@aws-cdk/core:aspectPrioritiesMutating": true,
90+
"@aws-cdk/aws-dynamodb:retainTableReplica": true,
91+
"@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true,
92+
"@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true,
93+
"@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true,
94+
"@aws-cdk/aws-s3:publicAccessBlockedByDefault": true
95+
}
96+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
testEnvironment: 'node',
3+
roots: ['<rootDir>/test'],
4+
testMatch: ['**/*.test.ts'],
5+
transform: {
6+
'^.+\\.tsx?$': 'ts-jest'
7+
}
8+
};

0 commit comments

Comments
 (0)