Mobile Development 18 min read

Applying Clean Architecture to a React Native International Flight Search List at Ctrip

The article describes how Ctrip's front‑end team refactored a complex React Native flight‑search list page using Clean Architecture, detailing the layered module design, TypeScript implementation, data flow, and why alternatives such as Redux, React components, and Hooks were rejected.

Ctrip Technology
Ctrip Technology
Ctrip Technology
Applying Clean Architecture to a React Native International Flight Search List at Ctrip

Preface

Ctrip's front‑end team migrated a large‑scale flight‑search list page from early exploration to full production using React Native, and then performed a major architectural refactor based on Clean Architecture principles.

1. GUI Architecture Review

The team compared traditional MV* patterns (MVC, MVP, MVVM) with Unidirectional data‑flow architectures like Redux, noting that Redux, while popular for web front‑ends, did not suit the complexity of their RN project, leading them to adopt Clean Architecture.

2. Clean Architecture

Clean Architecture, introduced by Uncle Bob, emphasizes framework‑independence, testability, UI‑independence, database‑independence, and agency‑independence. The system is organized into four concentric layers: Frameworks & Drivers, Interface Adapters, Application Business Rules, and Enterprise Business Rules, with dependencies pointing inward only.

The flight‑search list page’s business scenario includes massive code (70k+ lines), many dependent services, complex interactions, rich UI, and varying page structures, necessitating a modular, decoupled design.

2.1 Business Scenario

The list page must handle large data sets, multiple service calls, complex filtering, sorting, date switching, low‑price subscriptions, and dynamic UI changes across single‑trip, round‑trip, and multi‑city flows.

2.2 Application Structure

The project is split into a shared library (global utilities, native communication, networking) and business modules. Each business module follows the same Clean Architecture layout and can be nested, forming a fractal structure.

2.3 Module Structure

Each module contains four layers: ViewModel & StatelessView (UI), Presenter (connects ViewModel and Interactor), Interactor (orchestrates business use‑cases), and Model (independent business entities).

2.4 Code Implementation

Since 2017 the team has used TypeScript for its static typing, OOP friendliness, and IDE support. A typical module includes files such as ModuleBuilder.tsx , IViewModel.ts , ViewModel.tsx , StatelessView.tsx , IPresenter.ts , Presenter.tsx , IInteractor.ts , Interactor.ts , and Model.ts . The directory template is:

|____Builder
|   |____Builder.tsx
|   |____Page.tsx
|____BusStation
|   |____OneSimpleEventDescription.ts
|   |____OneSimpleApiDescription.ts
|____Contract
|   |____IViewModel.ts
|   |____IPresenter.ts
|   |____IModel.ts
|____Model
|   |____ModelOne.ts
|   |____ModelTwo.ts
|____Interactor
|   |____Interactor.ts
|____Presenter
|   |____Presenter.ts
|____ViewModel
|   |____ViewModel.tsx
|____View
|   |____StatelessView.tsx
|____Log
|   |____LogInfo.ts

Base classes such as JetModuleBuilder.tsx , JetViewModel.tsx , JetPresenter.tsx , and JetInteractor.tsx provide common functionality for all layers.

2.5 Data Flow

The internal data flow follows the Clean Architecture direction: Builder → ViewModel → Presenter → Interactor → Model, with bidirectional communication via API and event buses.

2.6 Concrete Case – Filter Module

The filter module demonstrates how UI elements (checkboxes, sidebars, result counts) trigger state changes that propagate through ViewModel, Presenter, Interactor, and Model, keeping UI and business logic decoupled.

2.7 Usability

To reduce boilerplate, the team provides standard template code for each layer, allowing developers to focus on business logic.

3. Why Not Use React Component Directly

Embedding state logic inside components leads to poor reuse, tangled lifecycle methods, and difficulty testing. The team therefore separates state from UI.

4. Why Not Use React Hook

Hooks improve state sharing but still suffer from complex component lifecycles and limited suitability for large‑scale business scenarios.

5. Why Not Use Redux

Redux’s single‑store approach becomes unwieldy for the massive state space of the flight list, causing low cohesion, string‑based action collisions, and difficulty extracting independent modules.

6. Summary

Adopting Clean Architecture for the international flight list page yielded a modular, testable codebase with 26 standard modules, a 71.3% reduction in bugs, 86% UI test coverage, and a 48.5% increase in development efficiency.

Appendix – Code Samples

// Confusing componentWillReceiveProps
public componentWillReceiveProps(nextProps) {
  if (
    nextProps.Filter_Model.changedTopFilter &&
    this.props.Filter_Model.changedTopFilter !== nextProps.Filter_Model.changedTopFilter &&
    nextProps.Filter_Model.changedTopFilter.length > 0
  ) {
    const directFlightOnly = this.props.Filter_Model.isDirectFlight;
    const changedList = new CompareLists().findDifferentItemNumber(
      this.flightListFilterObj.getOriginalTab(),
      nextProps.Filter_Model.changedTopFilter
    );
    this.hasFilterChanged = changedList.length > 0 || directFlightOnly;
  }
  if (nextProps.Filter_Model.selectedFilterCount !== this.props.Filter_Model.selectedFilterCount) {
    this.updateSelectedFilterCount(nextProps.Filter_Model.selectedFilterCount);
  }
  if (nextProps.Filter_Model.AirportList && !this.props.Filter_Model.AirportList) {
    this.setState({
      selectedFilterCount: this.getSelectedFilterCount(nextProps)
    });
  }
}
// Class Component Counter
class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return (
You clicked {this.state.count} times
this.setState({ count: this.state.count + 1 })}>
          Click me
);
  }
}
// Hook Counter
function Example() {
  const [count, setCount] = useState(0);
  return (
You clicked {count} times
setCount(count + 1)}>
        Click me
);
}
mobile developmentTypeScriptclean architecturefrontend architectureReact NativeModular Design
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.