Cinema Microservice — All Projects
Raihan
Available · --:--
cinema-microservice
$ cat README.md
# Cinema Microservice
Cinema-domain microservice backend with 6 services, 22 NATS message subjects, and an Ocelot gateway, built with .NET 8, Entity Framework Core, and Redis.
$ tree --dirsfirst -L 2
.
├── src/
│ ├── controllers/
│ ├── services/
│ ├── models/
│ └── middleware/
├── prisma/
├── docker-compose.yml
└── package.json
$ docker compose ps
✓ C#
✓ .NET 8
✓ Entity Framework Core
✓ MSSQL
✓ Redis
✓ NATS
✓ Ocelot
✓ Docker
Category
Internship Training
Year
2025
Stack
8 technologies

Cinema Microservice

C#.NET 8Entity Framework CoreMSSQLRedisNATSOcelotDocker

Overview

Cinema Microservice was a two-week internship training project in .NET. The brief was to learn C# and build a microservice backend for a cinema domain, so I split the system into 6 services behind an Ocelot API gateway: identity, studio, movie, schedule, ticket, and transaction. The services talk over NATS with 22 message subjects, and Redis backs identity tokens and seat locking. It was my first C# codebase, and it grew to 14,000 lines across 6 databases.

Problem

  • The training demanded working microservices in a language I had never used
  • Cinema booking spans movies, schedules, seats, and payments across separate services
  • Services needed to coordinate without a shared database or distributed transactions
  • The whole system had to route through a single API entry point

Goal

  • Learn C# and .NET while building real service separation
  • Use NATS for asynchronous communication between services
  • Add Redis for token revocation and seat locking
  • Route everything through an Ocelot API gateway

Role

I built the backend services independently during the training period, learning .NET while implementing the architecture. Team: Achmad Raihan Fahrezi Effendy (Backend Developer)

Architecture

flowchart TD
    G["Ocelot Gateway (5000)"] --> I["IdentityService (5001)"]
    G --> S["StudioService (5002)"]
    G --> M["MovieService (5003)"]
    G --> C["ScheduleService (5004)"]
    G --> T["TicketService (5005)"]
    G --> X["TransactionService (5006)"]
    N["NATS broker"] --- I
    N --- S
    N --- M
    N --- C
    N --- T
    N --- X
    I --> R1[("Redis: token store")]
    T --> R2[("Redis: seat lock")]
    I --> D1[("MSSQL: users")]
    S --> D2[("MSSQL: studios")]
    M --> D3[("MSSQL: movies")]
    C --> D4[("MSSQL: schedules")]
    T --> D5[("MSSQL: seats + tickets")]
    X --> D6[("MSSQL: transactions")]

Each service owns its database and exposes its domain through the gateway. Services coordinate over NATS, and Redis appears where it earns its place: identity token storage and ticket seat locking.

Database Design

Each service owns one table (Ticket owns two). Cross-service references are logical ids, with a single enforced foreign key from tickets to seats.

erDiagram
    seats ||--o{ tickets : "SeatId (FK)"

    users {
        int id PK
        string email UK
        string password_hash
        datetime created_at
        datetime deleted_at "soft delete"
    }
    studios {
        int id PK
        string name
        string address
        datetime deleted_at
    }
    movies {
        int id PK
        string title
        int duration_min
        string genre
        datetime deleted_at
    }
    schedules {
        int id PK
        int movie_id "logical ref"
        int studio_id "logical ref"
        datetime show_time
        datetime deleted_at
    }
    seats {
        int id PK
        int studio_id "logical ref"
        string seat_number
        string row_label
    }
    tickets {
        int id PK
        int seat_id FK
        int schedule_id "logical ref"
        int user_id "logical ref"
        string status
        datetime created_at
    }
    transactions {
        int id PK
        int ticket_id "logical ref"
        int user_id "logical ref"
        decimal total_amount
        string status
        datetime created_at
    }

Booking Flow

A booking moves across services through NATS. The ticket service reserves a seat with a 5-minute Redis lock, publishes ticket.created, and the transaction service consumes it to build the payment record.

sequenceDiagram
    participant U as Client
    participant G as Ocelot Gateway
    participant S as ScheduleService
    participant T as TicketService
    participant R as Redis
    participant N as NATS
    participant X as TransactionService

    U->>G: GET showtimes
    G->>S: schedule.get (request-reply)
    S-->>G: Showtimes
    U->>G: Reserve seat
    G->>T: Reserve
    T->>R: Lock seat (5 min TTL)
    R-->>T: locked
    T->>N: publish ticket.created
    N-->>X: consume ticket.created
    X->>X: Create transaction (pending)
    T-->>G: Reservation confirmed

API

Seventeen routes through the Ocelot gateway cover all 6 services.

Key Features

  • 6 Owned Services: Identity, Studio, Movie, Schedule, Ticket, and Transaction, each with its own database
  • NATS Messaging: 22 subjects, 6 request-reply and 16 publish-subscribe events
  • Ocelot Gateway: Single entry point routing to all services
  • JWT with JWKS: Identity issues RS256 tokens and exposes a JWKS metadata endpoint
  • Redis Token Store: Identity keeps active and revoked tokens in Redis
  • Redis Seat Lock: Tickets lock a seat for 5 minutes during reservation, with a 10-minute pending window
  • EF Core Migrations: Versioned schema per service with soft deletes

Technical Decisions

  • .NET 8 because the training required it, and it forced me to learn a new ecosystem quickly
  • Entity Framework Core as the standard .NET ORM, with one database per service
  • NATS over a heavier broker for lightweight pub-sub and request-reply
  • Ocelot as a minimal gateway appropriate for the training scope
  • Redis only where it pays off, token revocation and short-lived seat locks, instead of caching everywhere
  • Logical cross-service ids to keep services independent, with one physical FK only inside the ticket service

Challenges

  • Learning C#, LINQ, and async/await inside two weeks while building the whole system
  • Debugging message flow across 6 services required tracing NATS subjects by hand
  • Coordinating data across services without distributed transactions forced careful event design
  • Keeping one database per service meant the schema had to be duplicated across boundaries

Outcome

The training project produced a working microservice backend with NATS communication, Redis-backed seat locking, and a unified gateway. It proved that the patterns I knew from Node.js, dependency injection, service separation, and message brokers, carry straight into .NET. The system has no automated tests, and the docker-compose runs a subset of services, so the full stack runs best from the IDE.

Final Thoughts

Cinema Microservice taught me that the language matters less than the patterns. Dependency injection, service separation, and event-driven communication work the same in TypeScript, PHP, and C#. The two weeks were intense, and the breadth of the .NET ecosystem was worth the speed of learning. This project made me more adaptable across backend stacks.

Get In Touch

© 2026 Achmad Raihan Fahrezi Effendy Malang Regency, East Java · --:--
Hi! 👋 Ask me anything about this portfolio ✳