Skip to content

Latest commit

Β 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

NewsletterX

A production-grade newsletter subscription service built with C# / ASP.NET Core 8. This project demonstrates enterprise infrastructure patterns and best practices β€” the business idea is a vehicle to showcase that given any idea, the surrounding infrastructure is just a few tweaks.

🎯 Purpose

This is a portfolio project designed to demonstrate:

  • Clean layered architecture that scales
  • Production-ready AWS integration (SNS, SQS, DynamoDB)
  • Infrastructure patterns transferable to any microservice
  • Real-world concerns: connection pooling, idempotency, observability

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        NewsletterX.Host                          β”‚
β”‚                    (Composition Root)                            β”‚
β”‚         Program.cs | DI | Configuration | Background Services    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚
                                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        NewsletterX.Api                           β”‚
β”‚                      (HTTP Layer)                                β”‚
β”‚            Controllers | Middleware | Filters                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚
                                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     NewsletterX.Providers                        β”‚
β”‚                    (Business Logic)                              β”‚
β”‚     IAuthProvider | ISubscriptionProvider | ISnsPublisher        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚
                                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    NewsletterX.Repositories                      β”‚
β”‚                      (Data Access)                               β”‚
β”‚      IUserRepository | ISubscriptionRepository (PostgreSQL)      β”‚
β”‚              IEmailLogRepository (DynamoDB)                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β–Ό                               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  NewsletterX.EntityModels β”‚    β”‚   NewsletterX.Types       β”‚
β”‚   EF Core + DynamoDB      β”‚    β”‚  Enums | Constants        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Call Chain

HTTP Request β†’ Controller β†’ Provider β†’ Repository β†’ Database/AWS

πŸ“‹ Features

Feature Status Description
JWT Authentication πŸ”œ Register, login, protected routes
User Management πŸ”œ CRUD operations with soft delete
Subscriptions πŸ”œ Subscribe/unsubscribe to newsletters
SNS Fan-out πŸ”œ One publish β†’ multiple SQS queues
SQS Consumers πŸ”œ Background workers with DLQ
DynamoDB Audit πŸ”œ Append-only dispatch log with TTL
Rate Limiting πŸ”œ Protect auth endpoints
Health Checks βœ… DB + SQS connectivity checks
Structured Logging βœ… Serilog with correlation IDs

πŸ› οΈ Tech Stack

  • Runtime: .NET 8 / ASP.NET Core
  • Primary DB: PostgreSQL via EF Core
  • Audit Log: AWS DynamoDB
  • Messaging: AWS SNS + SQS
  • Auth: JWT Bearer tokens
  • Logging: Serilog (structured)
  • Local AWS: LocalStack
  • Containers: Docker

πŸš€ Quick Start

Prerequisites

1. Start Infrastructure

cd deployment
docker-compose up -d

This starts:

  • PostgreSQL on localhost:5432
  • LocalStack on localhost:4566 (SNS, SQS, DynamoDB)

2. Run the API

cd src/NewsletterX.Host
dotnet run

3. Open Swagger UI

Navigate to: http://localhost:5000

4. Verify LocalStack Resources

# List SNS topics
aws --endpoint-url=http://localhost:4566 sns list-topics

# List SQS queues
aws --endpoint-url=http://localhost:4566 sqs list-queues

# List DynamoDB tables
aws --endpoint-url=http://localhost:4566 dynamodb list-tables

πŸ“ Project Structure

NewsletterX/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ NewsletterX.Host/              # Entry point, DI, config
β”‚   β”‚   β”œβ”€β”€ Program.cs
β”‚   β”‚   β”œβ”€β”€ appsettings.json
β”‚   β”‚   β”œβ”€β”€ Configuration/             # Strongly-typed options
β”‚   β”‚   β”œβ”€β”€ BackgroundServices/        # SQS consumers, scheduled jobs
β”‚   β”‚   └── HealthChecks/
β”‚   β”‚
β”‚   β”œβ”€β”€ NewsletterX.Api/               # HTTP layer
β”‚   β”‚   β”œβ”€β”€ Controllers/
β”‚   β”‚   β”œβ”€β”€ Middleware/
β”‚   β”‚   └── Filters/
β”‚   β”‚
β”‚   β”œβ”€β”€ NewsletterX.Providers/         # Business logic
β”‚   β”‚   β”œβ”€β”€ Interfaces/
β”‚   β”‚   β”œβ”€β”€ Implementations/
β”‚   β”‚   β”œβ”€β”€ Options/                   # Provider-specific config
β”‚   β”‚   └── Messages/                  # SQS/SNS message DTOs
β”‚   β”‚
β”‚   β”œβ”€β”€ NewsletterX.Repositories/      # Data access
β”‚   β”‚   β”œβ”€β”€ Interfaces/
β”‚   β”‚   β”œβ”€β”€ Implementations/
β”‚   β”‚   └── Extensions/
β”‚   β”‚
β”‚   β”œβ”€β”€ NewsletterX.EntityModels/      # Persistence models
β”‚   β”‚   β”œβ”€β”€ PostgreSQL/                # EF Core entities
β”‚   β”‚   └── DynamoDB/                  # DynamoDB documents
β”‚   β”‚
β”‚   β”œβ”€β”€ NewsletterX.ContractModels/    # API DTOs
β”‚   β”‚   β”œβ”€β”€ Requests/
β”‚   β”‚   β”œβ”€β”€ Responses/
β”‚   β”‚   └── Validators/                # FluentValidation
β”‚   β”‚
β”‚   └── NewsletterX.Types/             # Shared primitives
β”‚       β”œβ”€β”€ Enums/
β”‚       └── Constants/
β”‚
β”œβ”€β”€ test/
β”‚   └── NewsletterX.Tests/
β”‚       β”œβ”€β”€ Unit/
β”‚       └── Integration/
β”‚
β”œβ”€β”€ deployment/
β”‚   β”œβ”€β”€ docker-compose.yml
β”‚   └── localstack-init/
β”‚       └── init-aws.sh                # Creates SNS/SQS/DynamoDB
β”‚
└── NewsletterX.sln

πŸ”‘ Key Patterns Demonstrated

1. Connection Pool Awareness

// PostgreSQL: Npgsql pools connections automatically
"Max Pool Size=50;Min Pool Size=5;Connection Idle Lifetime=300"

// DynamoDB: Client MUST be singleton (manages own HTTP pool)
services.AddSingleton<IAmazonDynamoDB, AmazonDynamoDBClient>();

2. SNS β†’ SQS Fan-out

NewsletterDispatchProvider.DispatchAsync()
        β”‚
        β–Ό
   SNS Topic (newsletter-dispatch)
        β”‚
        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό                β–Ό                β–Ό
   SQS Queue        SQS Queue       (future queues)
   (email-send)     (audit-log)
        β”‚                β”‚
        β–Ό                β–Ό
   EmailSender      AuditLogWorker
   Worker           β†’ DynamoDB

3. DynamoDB Key Design

Table: EmailDispatchLog
PK: NewsletterType (e.g., "weekly")
SK: DispatchTimestamp#DispatchId

TTL: Auto-delete after 90 days

4. Structured Logging with Correlation IDs

[10:23:45 INF] abc123 | Processing subscription for user@example.com
[10:23:46 INF] abc123 | Published to SNS: newsletter-dispatch
[10:23:47 INF] abc123 | Logged dispatch to DynamoDB

πŸ“š Architectural Decisions

Each file contains detailed comments explaining why decisions were made. Key documents:

  • src/NewsletterX.Host/appsettings.json - Configuration rationale
  • deployment/docker-compose.yml - Infrastructure decisions
  • deployment/localstack-init/init-aws.sh - AWS resource design
  • Project .csproj files - Dependency reasoning

πŸ§ͺ Testing

# Run all tests
dotnet test

# Run with coverage
dotnet test --collect:"XPlat Code Coverage"

πŸ“– License

MIT


Built to demonstrate production-grade infrastructure thinking. The newsletter is just the vehicle β€” the patterns are the product.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages