Skip to content

mzet97/DeveloperEvaluationProject

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Developer Evaluation Project

This repository contains a complete implementation of the sales challenge on top of the provided .NET 8 template.

The solution was implemented directly inside the existing repository structure, preserving the template conventions where they still made sense and tightening the areas that were incomplete or inconsistent.

Project Overview

The main deliverable is a sales API that supports:

  • creating sales
  • retrieving a sale by id
  • listing sales with pagination and sorting
  • updating sales
  • cancelling a sale
  • cancelling a sale item
  • structured event logging for sale lifecycle events

The implementation models sales as an explicit aggregate, persists denormalized external identities, enforces business rules inside the domain model, and includes unit, functional, and integration tests.

Architecture Overview

The backend lives in template/backend and follows a layered structure with vertical slices inside the application and API layers:

  • Ambev.DeveloperEvaluation.WebApi API contracts, controllers, middleware, HTTP composition
  • Ambev.DeveloperEvaluation.Application use cases, handlers, validators, mapping, event publishing abstraction
  • Ambev.DeveloperEvaluation.Domain aggregate root, entities, value objects, domain events, repository contracts
  • Ambev.DeveloperEvaluation.ORM EF Core context, mappings, repositories, migrations, database initialization
  • Ambev.DeveloperEvaluation.IoC dependency wiring and infrastructure registrations
  • tests unit, functional, and integration test suites

Layering intent

  • Domain contains business rules and invariants.
  • Application orchestrates use cases and publishes application events.
  • ORM handles persistence concerns only.
  • Web API stays thin and focuses on HTTP contracts and transport behavior.

Domain Overview

The core aggregate is:

  • Sale aggregate root, owns sale state, totals, cancellation state, and lifecycle rules
  • SaleItem child entity inside the aggregate, owns per-line quantity, pricing, discount, totals, and item cancellation

The aggregate stores cross-domain references using denormalized snapshots:

  • customer: CustomerId, CustomerName
  • branch: BranchId, BranchName
  • product: ProductId, ProductName

This preserves historical consistency even if the external customer, branch, or product data changes later.

Business Rules

Discounts are applied per identical sale item line, never globally across the whole sale:

  • quantity 1 to 3: 0%
  • quantity 4 to 9: 10%
  • quantity 10 to 20: 20%
  • quantity 21+: invalid

Additional rules:

  • a cancelled sale has zero effective total and all items cancelled
  • an individually cancelled item no longer contributes to the sale total
  • duplicate product lines in the same sale are rejected
  • sales are modeled with cancellation semantics instead of destructive deletion

Event Logging

The application publishes lifecycle notifications through IEventPublisher.

Current implementation:

  • LoggingEventPublisher writes structured logs

Published events:

  • SaleCreated
  • SaleModified
  • SaleCancelled
  • ItemCancelled

This keeps the architecture ready for a future broker-backed implementation without changing the application handlers.

Design Decisions

Cancellation vs delete

The challenge mentions CRUD, but destructive deletion is a weak fit for a sales domain. Sales were intentionally implemented with cancellation semantics instead of hard delete to preserve auditability and historical correctness.

Aggregate ownership

Pricing, discount calculation, quantity limits, item cancellation, and total recomputation are all enforced inside the aggregate instead of being spread across handlers or controllers.

Persistence strategy

EF Core mappings live in the ORM project through IEntityTypeConfiguration<T>. The domain model stays free of EF-specific attributes and persistence logic.

Startup behavior

The API initializes the database on startup, making local and containerized execution simpler for evaluation.

Tech Stack

  • C#
  • .NET 8
  • ASP.NET Core Web API
  • Entity Framework Core
  • PostgreSQL
  • MediatR
  • FluentValidation
  • AutoMapper
  • Serilog
  • xUnit
  • FluentAssertions
  • WebApplicationFactory
  • Testcontainers for PostgreSQL integration tests
  • Docker Compose

Project Structure

template/backend
├── src
│   ├── Ambev.DeveloperEvaluation.Application
│   │   ├── Common
│   │   └── Sales
│   ├── Ambev.DeveloperEvaluation.Common
│   ├── Ambev.DeveloperEvaluation.Domain
│   │   ├── Entities/Sales
│   │   ├── Events/Sales
│   │   ├── Repositories/Sales
│   │   └── ValueObjects/Sales
│   ├── Ambev.DeveloperEvaluation.IoC
│   ├── Ambev.DeveloperEvaluation.ORM
│   │   ├── Initialization
│   │   ├── Mapping/Sales
│   │   ├── Migrations
│   │   └── Repositories/Sales
│   └── Ambev.DeveloperEvaluation.WebApi
│       └── Features/Sales
├── tests
│   ├── Ambev.DeveloperEvaluation.Unit
│   ├── Ambev.DeveloperEvaluation.Functional
│   └── Ambev.DeveloperEvaluation.Integration
├── Dockerfile
├── docker-compose.yml
└── coverage-report.sh

API Endpoints Summary

Sales

  • POST /api/sales
  • GET /api/sales/{id}
  • GET /api/sales?pageNumber=1&pageSize=10&sortBy=soldAt&descending=true
  • PUT /api/sales/{id}
  • POST /api/sales/{id}/cancel
  • POST /api/sales/{saleId}/items/{saleItemId}/cancel

Template sample endpoints kept from the original repository

  • POST /api/users
  • GET /api/users/{id}
  • DELETE /api/users/{id}
  • POST /api/auth

Health

  • GET /health
  • GET /health/live
  • GET /health/ready

Example Request

POST /api/sales

{
  "saleNumber": "SALE-2026-0001",
  "soldAt": "2026-03-27T18:30:00Z",
  "customer": {
    "customerId": "11111111-1111-1111-1111-111111111111",
    "customerName": "John Doe"
  },
  "branch": {
    "branchId": "22222222-2222-2222-2222-222222222222",
    "branchName": "Sao Paulo Downtown"
  },
  "items": [
    {
      "productId": "33333333-3333-3333-3333-333333333333",
      "productName": "Beer",
      "quantity": 4,
      "unitPrice": 10.0
    },
    {
      "productId": "44444444-4444-4444-4444-444444444444",
      "productName": "Soda",
      "quantity": 10,
      "unitPrice": 5.0
    }
  ]
}

How To Run Locally

Prerequisites

  • .NET 8 SDK
  • Docker Desktop or Docker Engine available in WSL

Option 1: run database in Docker and API with dotnet run

cd template/backend
docker compose up -d db

export ConnectionStrings__DefaultConnection="Host=localhost;Port=5433;Database=developer_evaluation;Username=developer;Password=ev@luAt10n"
export Jwt__SecretKey="DeveloperEvaluationLocalSecretKeyThatIsLongEnough123456"

dotnet run --project src/Ambev.DeveloperEvaluation.WebApi/Ambev.DeveloperEvaluation.WebApi.csproj

API base URL:

  • http://localhost:5089 when using the default ASP.NET Core profile
  • or the URL printed by dotnet run

Stop the database:

docker compose down -v

Option 2: run the full stack with Docker Compose

cd template/backend
docker compose up -d --build

Verified endpoints:

  • API: http://localhost:8080
  • database host port: 5433
  • health check: http://localhost:8080/health

Stop the stack:

docker compose down -v

How To Run Tests

From template/backend:

dotnet test tests/Ambev.DeveloperEvaluation.Unit/Ambev.DeveloperEvaluation.Unit.csproj
dotnet test tests/Ambev.DeveloperEvaluation.Functional/Ambev.DeveloperEvaluation.Functional.csproj
dotnet test tests/Ambev.DeveloperEvaluation.Integration/Ambev.DeveloperEvaluation.Integration.csproj

Or run everything:

dotnet test Ambev.DeveloperEvaluation.sln

Coverage

Generate the HTML and text coverage report from template/backend:

./coverage-report.sh

Windows:

coverage-report.bat

Outputs:

  • text summary: template/backend/TestResults/CoverageReport/Summary.txt
  • HTML report: template/backend/TestResults/CoverageReport/index.html

Latest verified total line coverage:

  • 85.3%

Test Strategy

Unit tests

Focused on:

  • discount calculation boundaries
  • quantity limit enforcement
  • aggregate invariants
  • sale and item cancellation behavior
  • total recomputation
  • supporting domain and web helper components

Functional tests

Focused on:

  • HTTP contracts
  • status codes
  • request validation
  • error handling
  • sales flows
  • user/auth sample flows
  • health endpoints

Integration tests

Focused on:

  • real application startup
  • PostgreSQL persistence
  • EF Core mappings and repositories
  • end-to-end sales persistence behavior

Main Assumptions And Trade-Offs

  • Sales use cancellation instead of hard delete.
  • The original template user/auth sample was preserved and corrected where necessary, but the main evaluation focus is the sales domain.
  • Event publication is intentionally log-based today, with an abstraction designed for future broker replacement.
  • Generated EF migration artifacts remain in the repository and are included in the project, but the meaningful coverage target is achieved through application and domain behavior tests rather than generated code.
  • The template still emits a NuGet advisory warning for AutoMapper 13.0.1 during build and test. It does not block execution, but it is worth addressing in a follow-up dependency review.

About

This is a challenge by Coodesh

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages