You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于TDD与清洁架构的React Native住宿预订功能:网关设计及交互器实现优化问询

基于TDD与清洁架构的React Native住宿预订功能:网关设计及交互器实现优化问询

哥们,我完全懂你现在的困惑——在React Native里用清洁架构开发预订类功能时,很容易一不小心就把前端交互器写成了后端业务逻辑的翻版,尤其是涉及到一堆验证规则和错误处理的时候,写完一看:这怎么跟后端代码一模一样?别慌,咱们一步步梳理清楚。

首先得明确清洁架构里前端层和后端业务层的边界:你现在的BookAHousingInteractor里把大量后端该管的业务验证(比如入住日期不能早于今天、旅客数要符合房源的上下限)都硬塞进了前端,这就是问题根源。这些核心业务规则应该是后端的职责,前端只需要做轻量的前置校验(比如检查日期格式是否合法、旅客数是不是正整数),真正的业务验证交给后端去做,前端只负责把请求发过去,然后处理后端返回的结果。

关于你问的:要不要写带book方法的BookingGateway?

答案是必须要!这正是清洁架构里适配层的核心作用——把前端和后端的通信细节封装起来,给上层(交互器)提供清晰、易用的接口。

具体来说,你可以这么设计:

  1. 先定义一个BookingGateway接口,明确对外暴露的方法:
// 定义请求和响应类型
interface BookHousingRequest {
  housingId: string;
  checkInDate: Date;
  checkOutDate: Date;
  numberOfTravelers: number;
}

type BookHousingResult = 
  | { success: true; bookingId: string }
  | { success: false; errors: string[] };

interface BookingGateway {
  book(request: BookHousingRequest): Promise<BookHousingResult>;
}
  1. 然后实现一个ApiBookingGateway,负责实际的HTTP通信(用你项目里的请求库,比如axios、fetch或者React Native的fetch):
export class ApiBookingGateway implements BookingGateway {
  constructor(private authGateway: AuthenticationGateway) {}

  async book(request: BookHousingRequest): Promise<BookHousingResult> {
    try {
      const token = this.authGateway.getAuthenticatedUser()?.token;
      if (!token) {
        return { success: false, errors: ["Authentification obligatoire."] };
      }

      const response = await fetch('/api/bookings', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${token}`
        },
        body: JSON.stringify({
          housingId: request.housingId,
          checkInDate: request.checkInDate.toISOString(),
          checkOutDate: request.checkOutDate.toISOString(),
          numberOfTravelers: request.numberOfTravelers
        })
      });

      if (!response.ok) {
        const errorData = await response.json();
        return {
          success: false,
          errors: errorData.errors || ['预订失败,请稍后重试']
        };
      }

      const data = await response.json();
      return {
        success: true,
        bookingId: data.bookingId
      };
    } catch (error) {
      // 处理网络错误
      return {
        success: false,
        errors: ['网络连接失败,请检查网络后重试']
      };
    }
  }
}

重构你的BookAHousingInteractor

现在交互器的职责就清晰了:协调流程、做轻量前置校验、调用网关、把结果交给Presenter,而不是自己处理一堆业务规则。重构后的代码大概是这样:

export default class BookAHousingInteractor {
  constructor(
    private presenter: BookAHousingOutputPort,
    private bookingGateway: BookingGateway,
    private dateTimeProvider: DateTimeProvider
  ) {}

  async execute(requestModel: BookAHousingRequestModel): Promise<void> {
    // 前端轻量前置校验:只做格式/基础合法性检查
    const errors: string[] = [];
    const today = this.dateTimeProvider.now();
    
    if (requestModel.checkInDate < today) {
      errors.push("La date d'arrivé doit être égale ou supérieure à la date du jour.");
    }
    if (requestModel.checkOutDate <= today) {
      errors.push("La date de départ doit être supérieure à la date du jour.");
    }
    if (requestModel.numberOfTravelers < 1) {
      errors.push("Le nombre de voyageurs doit être au moins 1.");
    }

    if (errors.length > 0) {
      this.presenter.present({ errors });
      return;
    }

    // 调用网关发起预订请求
    const result = await this.bookingGateway.book({
      housingId: requestModel.housingId,
      checkInDate: requestModel.checkInDate,
      checkOutDate: requestModel.checkOutDate,
      numberOfTravelers: requestModel.numberOfTravelers
    });

    // 根据网关返回结果构建响应模型
    const responseModel: BookAHousingResponseModel = result.success
      ? { bookingId: result.bookingId }
      : { errors: result.errors };

    this.presenter.present(responseModel);
  }
}

测试策略怎么调整?

现在各层职责清晰,测试也变得简单多了:

  • 测试网关:用Jest Mock模拟fetch请求,测试不同场景:
    • 后端返回201成功时,网关是否正确返回带bookingId的成功结果;
    • 后端返回400错误时,网关是否正确解析错误数组;
    • 网络请求失败时,网关是否返回网络错误提示。
  • 测试交互器:MockBookingGateway和Presenter,测试:
    • 前置校验失败时,是否正确展示对应错误;
    • 网关返回成功时,是否通知Presenter展示预订成功;
    • 网关返回错误时,是否通知Presenter展示后端返回的错误列表;
    • 未登录场景(由网关处理)是否正确返回认证错误。
  • UI测试:用React Native Testing Library测试用户操作流程(比如填写表单、点击预订按钮),验证UI是否正确展示加载状态、成功提示或错误信息。

为什么只读功能没这个问题?

因为只读功能(比如获取房源列表、查看房源详情)通常只需要从后端拿数据,前端交互器的职责就是调用网关获取数据,然后交给Presenter展示,不会涉及到复杂的业务验证逻辑,所以边界感自然就清晰了。但预订这类写操作,很容易忍不住把后端的业务规则搬到前端,导致代码臃肿且和后端重复。

最后总结一下

核心就是明确各层职责:

  • 后端:负责核心业务规则验证、数据持久化;
  • 网关(适配层):封装前端和后端的通信细节,统一请求/响应格式,处理网络错误;
  • 交互器(应用层):协调前端流程,做轻量前置校验,调用网关,传递结果给Presenter;
  • Presenter(界面适配层):根据交互器的结果更新UI。

这样你的代码会更清晰,也更容易维护和测试,再也不会写出“像后端代码”的前端交互器啦!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 12:19:30