easy-model 领域驱动实践

在电商应用开发中,商品管理、购物车、订单处理等模块往往涉及复杂业务逻辑。easy-model 的模型驱动架构能有效组织这些领域知识,提升代码可维护性。本文通过实际用例展示如何使用 easy-model 实现电商领域的领域驱动开发。

商品管理模型

商品是电商的核心领域。我们可以创建一个 ProductModel 来封装商品状态和业务逻辑:

typescript 复制代码
import { useModel } from "easy-model";

class ProductModel {
  product = {
    id: "",
    name: "",
    price: 0,
    stock: 0,
    category: ""
  };

  constructor(initialProduct: typeof this.product) {
    this.product = initialProduct;
  }

  updateStock(newStock: number) {
    if (newStock < 0) throw new Error("库存不能为负");
    this.product.stock = newStock;
  }

  isAvailable() {
    return this.product.stock > 0;
  }

  applyDiscount(discountPercent: number) {
    this.product.price *= (1 - discountPercent / 100);
  }
}

function ProductCard({ productId }: { productId: string }) {
  const productModel = useModel(ProductModel, [{
    id: productId,
    name: "iPhone 15",
    price: 5999,
    stock: 10,
    category: "电子产品"
  }]);

  return (
    <div>
      <h3>{productModel.product.name}</h3>
      <p>价格: ¥{productModel.product.price}</p>
      <p>库存: {productModel.product.stock}</p>
      <p>状态: {productModel.isAvailable() ? "有货" : "缺货"}</p>
      <button onClick={() => productModel.updateStock(productModel.product.stock - 1)}>
        购买
      </button>
    </div>
  );
}

这个模型封装了商品的业务规则,如库存管理、折扣应用等。领域逻辑集中,避免了在组件中散布业务代码。

购物车共享状态

电商应用中,购物车需要在多个组件间共享。easy-model 天然支持按用户分组的实例缓存:

typescript 复制代码
import { useModel } from "easy-model";

class CartModel {
  items: Array<{ productId: string; quantity: number }> = [];
  userId: string;

  constructor(userId: string) {
    this.userId = userId;
  }

  addItem(productId: string, quantity: number) {
    const existing = this.items.find(item => item.productId === productId);
    if (existing) {
      existing.quantity += quantity;
    } else {
      this.items.push({ productId, quantity });
    }
  }

  getTotalItems() {
    return this.items.reduce((sum, item) => sum + item.quantity, 0);
  }
}

const CartProvider = provide(CartModel);

function CartIcon() {
  const cart = useModel(CartProvider, ["user123"]);
  return <div>购物车 ({cart.getTotalItems()})</div>;
}

function AddToCartButton({ productId }: { productId: string }) {
  const cart = useModel(CartProvider, ["user123"]);
  return (
    <button onClick={() => cart.addItem(productId, 1)}>
      加入购物车
    </button>
  );
}

通过 useModel,不同组件共享同一购物车实例,按用户ID分组,确保数据一致性。

订单异步处理

订单提交涉及异步操作。easy-model 的 @loader.load 装饰器简化加载状态管理:

typescript 复制代码
import { loader, useLoader, useModel } from "easy-model";

class OrderModel {
  order = { id: "", status: "pending", items: [] };

  constructor(orderId: string) {
    this.order.id = orderId;
  }

  @loader.load(true)
  async submitOrder() {
    // 模拟API调用
    await new Promise(resolve => setTimeout(resolve, 2000));
    this.order.status = "confirmed";
  }
}

function OrderForm() {
  const orderModel = useModel(OrderModel, ["order123"]);
  const { isLoading } = useLoader();

  return (
    <div>
      <p>订单状态: {orderModel.order.status}</p>
      <button
        onClick={() => orderModel.submitOrder()}
        disabled={isLoading}
      >
        {isLoading ? "提交中..." : "提交订单"}
      </button>
    </div>
  );
}

useLoader 自动跟踪异步方法状态,无需手动管理 loading 布尔值。

测试驱动开发

easy-model 的模型类易于测试,确保业务逻辑正确:

typescript 复制代码
import { describe, it, expect } from "vitest";

describe("ProductModel", () => {
  it("should update stock correctly", () => {
    const model = new ProductModel({
      id: "1",
      name: "Test",
      price: 100,
      stock: 5,
      category: "Test",
    });
    model.updateStock(3);
    expect(model.product.stock).toBe(3);
  });

  it("should throw on negative stock", () => {
    const model = new ProductModel({
      id: "1",
      name: "Test",
      price: 100,
      stock: 5,
      category: "Test",
    });
    expect(() => model.updateStock(-1)).toThrow();
  });
});

单元测试覆盖业务规则,提升代码质量。

总结

easy-model 在电商应用中展现出强大实力:模型封装领域逻辑、provide 支持状态共享、@loader.load 简化异步处理。结合 Vitest 测试,确保高质量交付。试试 easy-model,让电商开发更优雅!

项目地址:GitHub

相关推荐
Hilaku2 分钟前
OpenClaw 跟病毒的区别是什么?
前端·javascript·人工智能
jerrywus2 分钟前
AI 写代码总翻车?我用 Harness:developer 把它管成“右侧打工人”
前端·agent·claude
沸点小助手1 小时前
「国产龙虾谁能打过OpenClaw & 你敢让微信龙虾碰代码吗」沸点获奖名单公示|本周互动话题上新🎊
前端·后端·面试
skywalk81631 小时前
请学习kotti的前端(kotti其实是没有分离的前端的)实现,做到形似kotti那样的前端页面。
前端·学习
UI设计兰亭妙微1 小时前
兰亭妙微加载体验设计白皮书:从骨架屏到后台加载的全场景优化策略
前端·b端界面设计·ui设计公司
逆光如雪2 小时前
移动端卡片边框怎么做高级?我用 CSS 实现了设计师的刁钻要求
前端·css·vue.js
scan7242 小时前
龙虾读取session历史消息
java·前端·数据库
莹宝思密达2 小时前
地图显示西安经济开发区边界线-2023.12
前端·vue.js·数据可视化
lizhongxuan2 小时前
LLM Wiki:让大模型替你打理知识库的完整指南
前端·后端·面试
宇擎智脑科技2 小时前
Claude Code 源码分析(七):终端 UI 工程 —— 用 React Ink 构建工业级命令行界面
前端·人工智能·react.js·ui·claude code