0Pricing
TypeScript Academy · Урок

Моделирование предметной области — агрегаты, инварианты и службы

Моделируйте агрегаты, такие как Cart, с брендовыми идентификаторами и обеспечивайте инварианты с помощью фабрик и служб.

«Моделирование предметной области — агрегаты, инварианты и службы» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 3 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 3 уроков всего.

Введение

Цель: создать агрегат Cart с помощью идентификаторов с брендами и обеспечить соблюдение таких правил, как количество > 0, в фабриках и службах.

  • Идентификаторы с брендами различают границы сущностей
  • Фабрики возвращают объединения Result
  • Функции служб не допускают недопустимых переходов

Сущности и бренды

Идентификаторы с брендами делают CartId и ProductId несовместимыми, хотя оба являются строками. Это предотвращает ошибки смешения.

type Brand<Tag extends string, T> = T & { readonly __brand: Tag }

type CartId = Brand<"CartId", string>
type ProductId = Brand<"ProductId", string>

type Result<T> = { ok: true; value: T } | { ok: false; error: string }

const cartId = (s: string): CartId => s as CartId
const productId = (s: string): ProductId => s as ProductId

Агрегат и инвариант

Фабрики проверяют правила: количество должно быть > 0. Создавать можно только допустимые объекты Line.

type Money = number & { readonly __brand: "Money" }
const money = (n: number): Money => n as Money

interface Line { productId: ProductId; qty: number; price: Money }
interface Cart { id: CartId; lines: Line[]; total: Money }

function makeLine(productId: ProductId, qty: number, price: Money): Result<Line> {
  if (!Number.isInteger(qty) || qty <= 0) return { ok: false, error: "qty must be > 0" }
  return { ok: true, value: { productId, qty, price } }
}

function makeCart(id: CartId): Cart { return { id, lines: [], total: money(0) } }

Служба: добавление товара

Функции служб выполняют безопасные переходы и пересчитывают итоги. Если итоги недопустимы, функция возвращает ошибку вместо повреждения состояния.

function addItem(cart: Cart, line: Line): Result<Cart> {
  const lines = [...cart.lines, line]
  const totalNum = lines.reduce((s, x) => s + (x.qty * (x.price as number)), 0)
  if (!Number.isFinite(totalNum) || totalNum < 0) return { ok: false, error: "invalid total" }
  return { ok: true, value: { ...cart, lines, total: (totalNum as unknown) as Money } }
}

// Usage
const c0 = makeCart(cartId("c1"))
const l = makeLine(productId("p1"), 2, money(10))
const c1 = l.ok ? addItem(c0, l.value) : { ok: false, error: "bad line" }

Преобразование на границе

На границах системы (например, в APIs) преобразуйте данные в объекты передачи данных и проверяйте их при обратном разборе. Фабрики гарантируют корректность данных.

type CartDTO = { id: string; lines: { productId: string; qty: number; price: number }[]; total: number }

function serialize(c: Cart): CartDTO {
  return {
    id: c.id as unknown as string,
    lines: c.lines.map(x => ({ productId: x.productId as unknown as string, qty: x.qty, price: x.price as unknown as number })),
    total: c.total as unknown as number
  }
}

function parse(dto: CartDTO): Result<Cart> {
  const id = cartId(dto.id)
  const lines: Line[] = []
  for (const raw of dto.lines) {
    const l = makeLine(productId(raw.productId), raw.qty, money(raw.price))
    if (!l.ok) return { ok: false, error: l.error }
    lines.push(l.value)
  }
  const totalNum = lines.reduce((s, x) => s + (x.qty * (x.price as number)), 0)
  return { ok: true, value: { id, lines, total: money(totalNum) } }
}

Советы

Лучшие практики:

  • Централизуйте проверку в фабриках.
  • Делайте функции служб чистыми и детерминированными.
  • Преобразуйте бренды только на границах системы.

Проверка инварианта

Быстрая проверка: как следует обеспечивать соблюдение инварианта, например количество > 0?

Итоги

Итоги: идентификаторы с брендами в сочетании с фабриками и службами обеспечивают соблюдение инвариантов. Недопустимые состояния невозможно создать, поскольку все точки входа проверяют входные данные.

Часто задаваемые вопросы

Урок «Моделирование предметной области — агрегаты, инварианты и службы» бесплатный?

Да — полный текст урока «Моделирование предметной области — агрегаты, инварианты и службы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 3 уроков всего.

Чему я научусь в уроке «Моделирование предметной области — агрегаты, инварианты и службы»?

Моделируйте агрегаты, такие как Cart, с брендовыми идентификаторами и обеспечивайте инварианты с помощью фабрик и служб. Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать TypeScript Academy?

Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 3.

Сколько времени занимает урок «Моделирование предметной области — агрегаты, инварианты и службы»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке TypeScript Academy?

Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Брендированные и непрозрачные типы для предотвращения смешения единиц
  2. Идентификаторы с метками и шаблоны предметного моделирования
  3. Моделирование предметной области — агрегаты, инварианты и службы
← Назад к TypeScript Academy