WorkHow I workBeyond workContact
All work
Architecture

A booking module with a contract, migrated in 14 steps

Hotel booking platform, 1M+ users a month

Otello (2GIS)

2025 to 2026 · Team epic; the migration described here was my part

14

merge requests, each one shippable on its own

The problem

The service grew up with one lifecycle for order, booking and payment. Other domains read and wrote booking tables directly.

Any change to bookings could break orders or payments somewhere nobody expected, and every export or admin search carried its own copy of the booking logic.

What I did

  • Migrated every direct database query to bookings, from all domains, onto the booking module's contract: create, book, check confirmation, cancel, get, query.
  • Split the admin search into separate order and booking filters. One endpoint used to mix both.
  • Rebuilt the support exports on top of the contract, removed the heavy ad-hoc queries and, together with support, dropped the fields nobody used.

Decisions

  • Hexagonal architecture: the module owns its tables, everyone else talks to its interface.
  • Incremental migration: 14 small merge requests, each leaving the system working, instead of one big-bang rewrite.

What changed

  • Changes to bookings no longer break orders and payments.
  • Support exports got lighter and faster once the heavy queries were gone.
  • A clear module boundary: new booking features touch one place.

What went wrong

  • One of the migrations dropped a database column that another code path still read, and that caused a production incident. After the fix, the team tightened the release process for schema changes.

Stack

GoPostgreSQLHexagonal architecture