System Reconstruction of a Reservation Service: Design Patterns, Business Abstraction, and Deployment Strategies
This article describes the reconstruction of an order‑reservation‑refund system, detailing business background, abstraction of reservation flows, layered business classification, validation stack implementation, unified data models, mechanism‑strategy separation, and step‑by‑step deployment practices for a robust backend service.
The article revisits a previous discussion on service‑design‑pattern practices and explains how evolving business requirements demand more extensible and abstracted service architectures, especially for order, reservation, and refund systems that are tightly coupled with user transactions.
1. Project Business Background
The refactoring targets three services: order service (creation, state transition, payment), reservation system (supporting multiple reservation modes such as inventory, merchant‑accept, in‑service customer support, and direct merchant connection), and refund service (handling user, system, merchant, and customer‑service refunds with various rules).
The focus is on the reservation system because its redesign also covers techniques used for the order and refund services.
Goals
Abstract the reservation process and make it template‑driven.
Make the variable parts configurable.
Support seamless switching between old and new flows during rollout.
2. Business Abstraction
The reservation workflow is complex: an order may traverse different channels, and failures in one channel trigger fallback to another. Additional actions such as SMS notifications occur on success or failure, and data validation precedes the reservation.
From these observations the following abstractions are defined:
Primary business (Level‑1): the main reservation flow from start to success/failure, consisting of critical business nodes.
Reservation channels (e.g., inventory, merchant‑accept, customer‑service, direct merchant) represent different modes; transitions between channels are called channel flow.
Channel‑specific operations that affect the primary flow are classified as Level‑2 business.
Post‑process actions that do not affect the core flow (e.g., sending SMS, logging) are Level‑3 business.
3. Refactoring
3.1 Core Business Process
The core (Level‑1) process is modeled as a template engine where each node is an interchangeable interface, allowing configuration without code changes.
Example code for the reservation entry point:
public class KtvReserveService {
public KtvReserveResultDTO reserve(KtvReserveContext reserveContext) throws ReserveException {
// validation
KtvValidateResult validateResult = this.ktvReserveValidateStack.validate(reserveContext);
if (validateResult == null || !validateResult.isValid()) {
return KtvReserveResultDTO.createFailedResult("validate invalid");
}
// decide channel
KtvReserveChannel reserveChannel = reserveChannelJudgeService.judgeChannelType(reserveContext);
reserveDataService.store();
reserveDataService.transferReserveChannelStatus();
// start channel reservation
ChannelResult channelResult = this.reserveChannelService.reserve(reserveContext);
return KtvReserveResultDTO.genResult(channelResult.isSuccess(), channelResult.getDesc(), reserveFlow.getReserveId());
}
}Channel feedback handling:
public class KtvReplyReserveService {
@Override
public ReplyReserveResult reply(KtvReplyReserveInfo replyReserveInfo) throws ReplyReserveException {
// validation
KtvValidateResult validateResult = replyReserveValidateStack.validate(replyReserveInfo);
if (validateResult == null || !validateResult.isValid()) {
logger.warn(String.format(" %s validate failed", param));
return ReplyReserveResult.createFailedResult("validate failed");
}
// update status
reserveDataService.transferReserveChannelStatus();
// decide result
KtvReserveStatus toReserveStatus = this.reserveChannelJudgeService.judgeReserveResult(replyReserveInfo);
boolean reserveFailed = toReserveStatus == null || toReserveStatus == KtvReserveStatus.ReserveFailed || toReserveStatus == KtvReserveStatus.Init;
ReplyReserveResult result;
if (reserveFailed) {
result = this.reserveFailed(replyReserveInfo);
} else if (toReserveStatus == KtvReserveStatus.ReserveSuccess) {
result = this.reserveSuccess(replyReserveInfo);
} else {
result = ktvReserveTransferService.transferChannel(ktvReserveContext);
}
// channel internal processing
this.replyReserveChannelService.reply(replyReserveInfo);
return result;
}
}3.2 Validation Stack
Complex pre‑business validation is extracted into a validation stack built with the Chain of Responsibility pattern, assembled separately via a Proxy pattern to keep the main flow clean.
public interface KtvReserveValidateService {
/** Validate reservation information */
KtvValidateResult validate(KtvReserveContext reserveContext);
} public class KtvReserveValidateStack implements KtvReserveValidateService {
private List
validateServices;
public void setValidateServices(List
validateServices) {
this.validateServices = validateServices;
}
@Override
public KtvValidateResult validate(KtvReserveContext reserveContext) {
if (CollectionUtils.isEmpty(validateServices)) return KtvValidateResult.validResut();
for (KtvReserveValidateService service : validateServices) {
KtvValidateResult result = service.validate(reserveContext);
if (result == null || !result.isValid()) return result;
}
return KtvValidateResult.validResut();
}
}3.3 Business Layering
Level‑1 business steps must be strictly reliable; failures abort the transaction. Level‑2 business is important but should not affect the final outcome; errors trigger alerts or asynchronous compensation. Level‑3 business is completely decoupled and can be executed asynchronously (e.g., SMS notifications) using the Observer pattern.
3.4 Unified Data Model
A single data contract (e.g., ReserveContext ) is used throughout the flow, preventing duplicated reads and simplifying state management.
3.5 Mechanism‑Strategy Separation
Following the classic Unix principle, core algorithms (mechanism) are separated from interfaces (strategy). This mirrors the MVC pattern and allows independent evolution of strategies such as how reservation results are reported without touching the engine.
4. Deployment Strategy
Two refactoring approaches are discussed: internal logic changes without new tables (easy rollout) and interface changes with new tables (requires compatibility handling). The article recommends incremental, step‑by‑step releases, validating each piece before proceeding.
5. Summary
Key principles applied during the reconstruction include mechanism‑strategy separation, unified protocol design, and the Open‑Closed principle. The design patterns employed are Proxy, Observer, Chain of Responsibility, and Decorator.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.