Skip to content

joaohmalves/RTBHouse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RTB House Technical Test

A full-stack web application developed as part of the RTB House technical assessment.


Tech Stack

Backend

  • NestJS with TypeScript
  • Repository pattern with dependency injection
  • JSON as persistence layer
  • class-validator for request validation
  • Swagger for API documentation

Frontend

  • React with TypeScript
  • Vite
  • React Query
  • Recharts

Infrastructure

  • Docker + Docker Compose

Architecture

The backend follows a layered architecture inspired by clean architecture principles:

HTTP Request
    ↓
Controller       — routes requests, no business logic
    ↓
Service          — business rules and data aggregation
    ↓
Repository       — data access abstraction
    ↓
JSON files       — persistence layer (orders.json / sellers.json)

Repository Pattern

The repository layer is abstracted behind interfaces (IOrdersRepository, ISellersRepository). The service layer depends only on these interfaces — never on concrete implementations. This means the persistence layer can be swapped (e.g. JSON → PostgreSQL) without any changes to the service or controller layers.

// Service depends on the interface, not the implementation
@Inject('IOrdersRepository')
private readonly ordersRepository: IOrdersRepository

Project Structure

rtb-house/
├── docker-compose.yml
├── README.md
├── backend/
│   ├── Dockerfile
│   └── src/
│       ├── main.ts
│       ├── app.module.ts
│       ├── common/
│       │   ├── enums/
│       │   │   └── order-sort.enum.ts
│       │   └── interfaces/
│       │       └── paginated-result.interface.ts
│       ├── orders/
│       │   ├── dto/
│       │   │   └── get-orders-query.dto.ts
│       │   ├── enums/
│       │   │   └── order-sort-by.enum.ts
│       │   ├── interfaces/
│       │   │   └── orders.interface.ts
│       │   ├── repositories/
│       │   │   ├── orders.repository.interface.ts
│       │   │   └── json-orders.repository.ts
│       │   ├── utils/
│       │   │   └── order.utils.ts
│       │   ├── orders.controller.ts
│       │   ├── orders.service.ts
│       │   └── orders.module.ts
│       ├── sellers/
│       │   ├── dto/
│       │   │   └── get-sellers-query.dto.ts
│       │   ├── enums/
│       │   │   └── seller-sort-by.enum.ts
│       │   ├── interfaces/
│       │   │   └── sellers.interface.ts
│       │   ├── repositories/
│       │   │   ├── sellers.repository.interface.ts
│       │   │   └── json-sellers.repository.ts
│       │   ├── sellers.controller.ts
│       │   ├── sellers.service.ts
│       │   └── sellers.module.ts
│       └── data/
│           ├── orders.json
│           └── sellers.json
└── frontend/
    ├── Dockerfile
    └── src/
        ├── components/
        │   ├── charts/
        │   │   ├── chart.constants.ts
        │   │   ├── charts.css
        │   │   ├── SalesByCountryChart.tsx
        │   │   └── SalesBySellerChart.tsx
        │   ├── OrderFilters.tsx
        │   ├── OrdersTable.tsx
        │   ├── Pagination.tsx
        │   └── SellerCards.tsx
        ├── hooks/
        │   ├── useOrders.ts
        │   └── useSellers.ts
        ├── pages/
        │   ├── InfoPage.tsx
        │   └── OrdersPage.tsx
        ├── services/
        │   └── api.ts
        ├── types/
        │   ├── order.types.ts
        │   └── seller.types.ts
        ├── utils/
        │   └── export.utils.ts
        ├── App.tsx
        ├── main.tsx
        └── index.css

Getting Started

Running with Docker (recommended)

docker-compose up --build
Service URL
Frontend http://localhost:5173
Backend http://localhost:3000
Swagger http://localhost:3000/api/docs

Running locally

Backend

cd backend
npm install
npm run start:dev

Frontend

cd frontend
npm install
npm run dev

Features

  • Orders table with dynamic filtering by seller, country, price range
  • Server-side pagination and sorting on all columns
  • Revenue charts by seller and by country
  • Seller revenue cards with order count
  • CSV export of filtered results
  • Country flags with local currency formatting
  • Order ID parsed to display creation date
  • Dark/light theme toggle
  • Responsive layout with horizontal scroll on mobile
  • Swagger API documentation

API Reference

Full interactive documentation available at:

http://localhost:3000/api/docs

Orders

Method Endpoint Description
GET /api/v1/orders List orders with optional filters
GET /api/v1/orders/:id Get order by ID

Query params for GET /api/v1/orders:

Param Type Description
seller number Filter by seller ID
country string Filter by country code (BRA, ARG, MEX)
minPrice number Minimum price
maxPrice number Maximum price
sortBy enum orderId, product, seller, country, price, createdAt
order enum asc, desc
page number Page number (default: 1)
limit number Items per page (default: 10)

Sellers

Method Endpoint Description
GET /api/v1/sellers List sellers with optional filters
GET /api/v1/sellers/:id Get seller by ID

Query params for GET /api/v1/sellers:

Param Type Description
name string Filter by seller name
sortBy enum id, name
order enum asc, desc
page number Page number (default: 1)
limit number Items per page (default: 10)

Security

  • CORS configured for frontend origin only
  • Helmet for HTTP security headers
  • ValidationPipe with whitelist: true — strips undeclared fields from requests
  • forbidNonWhitelisted: true — rejects requests with unknown fields
  • transform: true — automatic type coercion for query params

Design Decisions

Why NestJS over Express? NestJS enforces a modular, layered architecture out of the box — controllers, services, and dependency injection are first-class citizens. This mirrors patterns from Spring Boot that I have experience with other projects, making the codebase predictable and easy to navigate for any backend developer.

Why Repository Pattern? Decouples business logic from data access. The service layer depends on interfaces, not implementations — this is the Dependency Inversion Principle (SOLID) applied in practice.

Why React Query? Handles caching, loading and error states cleanly. The placeholderData option keeps previous results visible during refetch, preventing layout shifts when filters or sort change.

createdAt parsed at repository level The orderId encodes the creation date (20190600012019-06). Parsing happens in the repository constructor so the field exists before any sorting or filtering — enabling sort by date without recalculating on every query.


About the Developer

João Henrique Monteiro Alves

This project was developed as part of the RTB House technical assessment.

What was built

  • REST API with NestJS and TypeScript following clean architecture principles
  • Repository pattern with dependency injection for persistence abstraction
  • Dynamic filtering, pagination and sorting on all endpoints
  • Currency formatting per country and order date parsing from order ID
  • Swagger documentation at /api/docs
  • Frontend with React, TypeScript, React Query and Recharts
  • Revenue dashboards with charts by seller and country
  • CSV export of filtered results
  • Dark/light theme toggle with full responsiveness
  • Docker + Docker Compose for containerization

Contact

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors