前端整洁架构,你了解多少?( 四 )


实现细节:领域层一旦确定了需要的实体,就可以开始定义它们的行为 。
接下来将展示项目中的代码结构 。为了清晰起见,将代码分成了不同的文件夹-层级进行组织:
src/|_domAIn/|_user.ts|_product.ts|_order.ts|_cart.ts|_Application/|_addToCart.ts|_authenticate.ts|_orderProducts.ts|_ports.ts|_services/|_authAdapter.ts|_notificationAdapter.ts|_paymentAdapter.ts|_storageAdapter.ts|_api.ts|_store.tsx|_lib/|_ui/领域层位于domain/目录,应用层位于application/目录,适配器位于services/目录 。我们将在最后讨论该代码结构的替代方案 。
创建领域实体在领域中有4个模块:

  • 产品
  • 用户
  • 订单
  • 购物车
主要的参与者是用户,想要在会话期间将用户数据存储在storage中 。为了对这些数据进行类型化,需要创建一个名为"User"的领域实体 。
User 实体将包含ID、姓名、邮箱以及喜好和过敏列表 。
// domain/user.tsexport type UserName = string;export type User = {id: UniqueId;name: UserName;email: Email;preferences: Ingredient[];allergies: Ingredient[];};用户将商品放入购物车中 。下面来为购物车和产品添加类型 。购物车项将包含ID、名称、以分为单位的价格和成分列表 。
// domain/product.tsexport type ProductTitle = string;export type Product = {id: UniqueId;title: ProductTitle;price: PriceCents;toppings: Ingredient[];};在购物车中会保留用户放入其中的产品列表:
// domain/cart.tsimport { Product } from "./product";export type Cart = {products: Product[];};在成功支付后,会创建一个新的订单 。可以添加一个名为Order的实体类型 。Order类型将包含用户ID、已订购产品列表、创建日期和时间、订单状态以及整个订单的总价格 。
// domain/order.tsexport type OrderStatus = "new" | "delivery" | "completed";export type Order = {user: UniqueId;cart: Cart;created: DateTimeString;status: OrderStatus;total: PriceCents;};检查实体之间的关系以这种方式设计实体类型的好处是可以检查它们的关系图是否符合实际情况:
前端整洁架构,你了解多少?

文章插图
实体关系图我们可以查看和检查以下内容:
  • 主要参与者是否真的是用户 。
  • 订单中是否包含足够的信息 。
  • 是否需要扩展某些实体 。
  • 将来是否会出现可扩展性问题 。
此外,在这个阶段类型将有助于突出显示实体之间的兼容性以及实体之间信号方向的错误 。如果一切符合期望,就可以开始设计领域变换了 。
创建数据转化上面设计的类型的数据将经历各种各样的处理 。我们将向购物车中添加商品、清空购物车、更新商品和用户名称等 。我们将为所有这些转换创建单独的函数 。
例如,要确定用户是否对某个成分或喜好过敏,可以编写函数hasAllergy和hasPreference:
// domain/user.tsexport function hasAllergy(user: User, ingredient: Ingredient): boolean {return user.allergies.includes(ingredient);}export function hasPreference(user: User, ingredient: Ingredient): boolean {return user.preferences.includes(ingredient);}函数 addProduct 和 contains 用于将商品添加到购物车并检查商品是否在购物车中:
// domain/cart.tsexport function addProduct(cart: Cart, product: Product): Cart {return { ...cart, products: [...cart.products, product] };}export function contains(cart: Cart, product: Product): boolean {return cart.products.some(({ id }) => id === product.id);}我们还需要计算产品列表的总价格,为此需要编写函数totalPrice 。如果需要,可以在这个函数中添加各种条件来考虑促销码或季节性折扣等 。
// domain/product.tsexport function totalPrice(products: Product[]): PriceCents {return products.reduce((total, { price }) => total + price, 0);}为了让用户能够创建订单,我们需要编写函数createOrder 。它将返回与指定用户和其购物车关联的新订单 。
// domain/order.tsexport function createOrder(user: User, cart: Cart): Order {return {cart,user: user.id,status: "new",created: new Date().toISOString(),total: totalPrice(products),};}注意,在每个函数中,我们都构建了 API,以便我们可以轻松地转换数据,函数接受参数并按照希望的方式给出结果 。


推荐阅读