Problem
I built a small API to store long URLs under short codes and redirect those codes to the original address. The aim was to separate validation, persistence and application logic into clear responsibilities.
My contribution
I implemented creation, lookup and HTTP 302 redirect endpoints with .NET 8 and ASP.NET Core. I separated Domain, Application, Infrastructure and API layers and organized CQRS workflows with MediatR. I added PostgreSQL/EF Core persistence, FluentValidation, xUnit/Moq tests, Docker Compose and structured logging.
Architecture
This diagram shows request flow and layer responsibilities.
POST /api/urls
→ API → Application: validation + MediatR handler
→ Domain: URL entity / repository contract
→ Infrastructure: EF Core → PostgreSQL
GET /r/{shortCode}
→ Query handler → found: HTTP 302
missing: HTTP 404
One decision and its tradeoff
I moved creation and lookup logic out of the controller into handlers and repository contracts. This makes workflows easier to test separately from HTTP and data access. It also adds files and dependency wiring for an API of this size.
Evidence
For the source review dated , inspectable evidence includes the public repository, 302 redirect controller and test sources. Test files do not demonstrate a current successful run; tests were not rerun for this work.
Limitations
There is no live demo. The rate-limit partition key falls back to Host for anonymous requests, leaving per-client separation incomplete. Checking a short code before saving it does not by itself resolve concurrent requests selecting the same code; retry after an insert collision is missing. CI setup/verification remains incomplete, with no successful CI result offered. These gaps were not fixed here.