A Twitter-like social media platform built as a Technical Assessment. Users can create posts, repost content, search, and browse a feed with infinite scrolling.
- Framework: ASP.NET Core (.NET 10) Web API
- Database: SQL Server 2022
- ORM: Entity Framework Core
- API Docs: Swagger/OpenAPI
- Architecture: Clean Architecture (Core, Infrastructure, Api layers)
- Framework: React 18 with TypeScript
- Build Tool: Vite
- UI Library: Material UI (MUI)
- State Management: TanStack Query (React Query) + React Context
- Architecture: Atomic Design (atoms, molecules, organisms, templates, pages)
- Database: Docker container (SQL Server 2022)
- Containerization: Docker Compose for full-stack deployment
- CI/CD: GitHub Actions (lint, build, test)
- Testing: xUnit with Moq
- Docker and Docker Compose
- Node.js (v20+) - for local frontend development
- .NET 10 SDK - for local backend development
git clone <repository-url>
cd posterr
docker compose up --buildThis starts:
- SQL Server on port 1433
- Backend API on port 5000
- Frontend on port 3000
- Frontend: http://localhost:3000
- Backend API: http://localhost:5000/api
- Swagger Docs: http://localhost:5000/swagger
The database is automatically seeded with 4 demo users:
alice_johnson(default)john_doejane_smithbob_wilson
docker compose up sqlservercd src/Posterr.Api
dotnet runThe backend starts on http://localhost:5000 and automatically runs migrations and seeds the database.
cd frontend
npm install
npm run devThe frontend starts on http://localhost:5173 by default.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/posts |
Get paginated posts |
POST |
/api/posts |
Create a new post |
POST |
/api/posts/:id/repost |
Repost an existing post |
Query Parameters for GET /api/posts:
page(int, default: 1)limit(int, default: 15)sort(string: "latest" or "trending", default: "latest")search(string, optional)
Headers:
Content-Type: application/jsonX-User-ID: <user-guid>(required for POST requests)
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/users |
Get all users |
- Daily Post Limit: Maximum 5 posts per user per day (including reposts)
- Character Limit: Posts have a maximum of 777 characters
- Repost Rules:
- Only original posts can be reposted (not reposts)
- Users cannot repost their own posts
- Users cannot repost the same post twice
- Users must confirm before reposting
- Search: Only searches original posts (not reposts), exact keyword match
- Sorting: "Latest" (by creation date desc) or "Trending" (by repost count desc)
- Pagination: Initial load shows 15 posts, subsequent loads show 20 posts
# From the root directory
dotnet test
# With verbose output
dotnet test --verbosity normalTests cover all business logic in the PostService including:
- Post creation (valid, empty, too long, daily limit)
- Reposting (valid, own post, repost of repost, duplicate, daily limit)
- Feed pagination
posterr/
├── src/
│ ├── Posterr.Api/ # ASP.NET Core Web API
│ │ ├── Controllers/ # API endpoints
│ │ ├── Program.cs # App configuration and DI
│ │ └── Dockerfile
│ ├── Posterr.Core/ # Domain layer (no dependencies)
│ │ ├── Entities/ # User, Post models
│ │ ├── DTOs/ # Data transfer objects
│ │ ├── Interfaces/ # Repository & service contracts
│ │ └── Services/ # Business logic (PostService)
│ └── Posterr.Infrastructure/ # Data access layer
│ ├── Data/ # DbContext, migrations, seeder
│ └── Repositories/ # EF Core repository implementations
├── tests/
│ └── Posterr.Tests/ # Unit tests (xUnit + Moq)
├── frontend/ # React + TypeScript SPA
│ ├── src/
│ │ ├── components/
│ │ │ ├── atoms/ # CharCounter, RepostLabel
│ │ │ ├── molecules/ # PostHeader, PostActions, SearchForm, SortControls
│ │ │ ├── organisms/ # CreatePost, PostCard, PostFeed
│ │ │ └── templates/ # MainLayout
│ │ ├── contexts/ # UserContext (React Context)
│ │ ├── hooks/ # Custom hooks (usePosts, useCreatePost, useRepost)
│ │ ├── lib/ # TanStack Query client config
│ │ ├── pages/ # HomePage
│ │ ├── services/ # API client
│ │ └── types/ # TypeScript interfaces
│ └── Dockerfile
├── docker-compose.yml
└── Posterr.sln
- Clean Architecture: Core layer has zero dependencies, Infrastructure implements data access, API handles HTTP concerns. This makes the business logic testable in isolation.
- Repository Pattern: Database operations are separated from business logic (PostRepository, UserRepository), addressing the concern of "database operations handled at the use-case level."
- Users from API: User data comes from the database via the API, not hardcoded IDs in the frontend.
- Early Return Pattern: Used throughout to avoid nested conditionals and improve readability.
- Rate Limiting: ASP.NET Core rate limiter middleware protects post creation endpoints from abuse, with proper 429 error responses.
- Atomic Design: Frontend components are organized into atoms, molecules, organisms, templates, and pages for clear separation of concerns and reusability.
- TanStack Query: Server state management with automatic caching, background refetching, and infinite scroll pagination via
useInfiniteQuery.
Auth: Right now users are identified via a X-User-ID header, which is obviously not production-ready. JWT with refresh tokens or an identity provider (Auth0, Azure AD) would be the next step.
Real-time feed: The feed requires a page refresh to see new posts. SignalR would solve this — live updates when new posts come in, notifications on reposts, etc.
Better search: Search is just LIKE queries right now. SQL Server has built-in Full-Text Search that handles stemming and relevance ranking, which would make the experience much better.
Frontend tests: No component tests yet. React Testing Library + Vitest would cover the main user flows (creating posts, reposting, error states).
Integration tests: Only unit tests exist. WebApplicationFactory would let me test the full API pipeline end-to-end against a real database.
Observability: Structured logging (Serilog), health checks, and APM integration (Application Insights or Datadog) for production monitoring.
The database would be the first bottleneck. The "trending" sort counts reposts across all posts, and OFFSET/FETCH pagination gets slower as data grows.
How I'd tackle it:
-
Query optimization first — switch to cursor-based pagination (keyset using
CreatedAt+Id), materializeRepostCountas a column instead of counting via joins, and add Redis caching for hot data like the trending feed. -
Scale the API horizontally — the stateless design already supports this. Add distributed caching with Redis and move rate limiting to an API gateway.
-
Database scaling — read replicas for feed queries, table partitioning by date on Posts, and potentially Elasticsearch for search to take that load off SQL Server.
-
Bigger architectural changes (only if needed) — message queue for async processing, CQRS to separate reads/writes, and microservices only when the monolith actually becomes a deployment bottleneck for the team.