Пришло время погасить, я надеюсь, что все в безопасности в эпидемии, и компания все еще там!
SKR Shop - это группа низкоуровневых кодеров. Из-за безумия подвергается пыткам проектом на работе, а из-за гордости программиста: системы, разработанные другими, все дерьмо, и мой дизайн самый удивительный Вселенная, поэтому я решил сделать это. Сделайте руководство по проектированию электронной коммерции, которые только дизайны без кодирования.
Адрес проекта: https://github.com/skr-shop/manuals
в предыдущей статьеАнализ спроса на дизайн корзины покупокОписываются общие требования к тележкам для покупок. Основное внимание в этой статье уделяется тому, как реализовать архитектурный проект (бизнес + системная архитектура).
инструкция
Архитектурное проектирование можно разделить на три уровня:
- бизнес-архитектура
- структура системы
- Технологическая архитектура
Быстро и просто объясним смысл следующих трех архитектур; когда мы получаем требования к корзине, мы говорим, что используем Golang для ее реализации и используем Redis для хранения; это описывает техническую архитектуру; мы наслаиваем код корзины проект, проектные спецификации и планирование зависимых систем называются системной архитектурой;
Что такое бизнес-архитектура? Бизнес-архитектура — это буквальное языковое описание архитектуры системы, что это значит? Когда мы получаем запрос, мы должны сначала связаться с заявителем, чтобы установить единое познание. Например: нормативные существительные (товары в корзине и товары в товарной системе имеют разное значение); построить модель, понятную каждому, взаимодействие между корзиной, пользователем, товаром, заказом и этими сущностями, и то, что каждый имеет особенности.
Существует много методологий анализа бизнес-архитектуры, таких как проектирование на основе предметной области, но это не единственный и не лучший метод анализа бизнес-архитектуры. Что подходит вам лучше всего. Наши часто используемые диаграммы отношений сущностей и диаграммы UML также относятся к области бизнес-архитектуры;
Что здесь должно быть сильным, так это то, что независимо от того, какой метод вы используете для моделирования дизайна, лучше иметь дизайн, чем его отсутствие Во-вторых, содержание моделирования должно быть отражено в вашем коде.
Анализ бизнес-архитектуры в этой статье опирается наDDD
(Domain Driven Design) мышление; все еще это предложение适合的就是最好的
.
бизнес-архитектура
Благодаря предыдущему анализу спроса мы выяснили, что будет делать наша корзина. Давайте взглянем на типичный процесс работы с корзиной покупок.
В этом процессе пользователь использует корзину для покупок в качестве носителя для завершения процесса покупки продукта; постоянно поступающие данные — это продукт, а корзина для покупок — стабильный носитель. Это точка стабильности и изменения в нашей системе.
Поток товаров может быть различным, например добавление в корзину из разных мест, добавление в корзину разными способами, и жизненный цикл в корзине разный, но этот процесс стабилен, и товары должны быть. в корзине, затем можно перейти в населенный пункт для формирования заказа.
Жизненный цикл товара в корзине выглядит следующим образом:
Следуя этому процессу, давайте рассмотрим операции, соответствующие каждому этапу.
Обратите внимание, что эта операция перед добавлением корзины фактически может быть помещена в операцию добавления корзины, а потому эта часть очень нестабильна и изменчива. Мы выделяем его, чтобы облегчить последующее расширение, не влияя на относительно стабильную стадию корзины покупок.
Вышеупомянутые три этапа, согласно концепции в DDD, должны называться сущностями, и они составляют домен корзины в целом, сегодня мы пропустим эти понятия, не говоря о них, и у нас будет возможность разместить их отдельно. позже.
перед добавлением
Посредством анализа процесса мы обобщили операционные интерфейсы, которые должны быть в системе, и сущности, соответствующие этим интерфейсам.Теперь давайте посмотрим, что нужно сделать перед добавлением автомобиля;
Перед добавлением корзины в основном проверяются различные широты добавляемых продуктов корзины, чтобы проверить, соответствуют ли они требованиям.
Прежде чем позволить пользователям добавлять автомобили, мы сначала решаем, откуда пользователи продают, а затем проверяем их? Потому что бывают разные ситуации, когда один и тот же продукт приобретается по разным каналам, например: мобильный телефон Xiaomi, независимо от того, покупаем ли мы его через seckill, через краудфандинг друзей или покупаем напрямую в торговом центре, цена отличается, но в на самом деле это один и тот же продукт;
Второй вопрос, имеете ли вы право на покупку, или как было сказано выше, добавление автомобилей в спайк и краудфандинговые операции, не все могут их добавить, они должны иметь действующую квалификацию. Затем сюда же ставится квалификационная проверка;
Третий вопрос заключается в проверке атрибутов приобретенного продукта, например, есть ли он на полке или нет, в наличии, ограниченное количество покупки и т. д.
И вы обнаружите, что условия проверки здесь могут быть очень изменчивыми. Как создать код, который легко расширять?
Во всем процессе загрузки автомобиля важно различать различные проверки в зависимости от источника. У нас есть два варианта.
Способ 1: Это делается через режим стратегии + режим фасада. Стратегия состоит в том, чтобы выполнять различные проверки в соответствии с различными источниками автомобилей, а фасад состоит в том, чтобы инкапсулировать каждую стратегию в соответствии с различными источниками;
Метод 2: через модель цепочки ответственности, но здесь необходимо внести изменения.В процессе выполнения этой цепочки вы можете пропустить определенные узлы, например: Seckill не требует инвентаризации или проверки краудфандинга;
В результате всестороннего анализа я выбрал модель цепочки ответственности. Вставьте основной код
// 每个验证逻辑要实现的接口 type Handler interface { Skipped(in interface{}) bool // 这里判断是否跳过 HandleRequest(in interface{}) error // 这里进行各种验证 }
// 责任链的节点 type RequestChain struct { Handler Next *RequestChain }
// 设置handler func (h *RequestChain) SetNextHandler(in *RequestChain) *RequestChain { h.Next = in return in }
Шаблоны проектирования можно найти на github моего друга: https://github.com/TIGERB/easy-tips/tree/master/go/src/patterns.
корзина
Прежде чем мы закончим добавлять корзины, давайте посмотрим на часть корзины покупок. Ранее мы обсуждали, что корзины для покупок могут иметь различные формы, например: хранить несколько товаров для совместного расчета и немедленный расчет определенного товара. Поэтому корзину необходимо выбирать в соответствии с типом канала.
Эта часть операции относительно стабильна. Давайте выберем еще несколько важных операций, чтобы поговорить об этой идее.
добавить в корзину
Предваряя условную проверку, вы обнаружите, что эта часть логики стала очень легкой при добавлении автомобиля. Главное сделать логику следующих частей.
Здесь есть несколько хитростей.Первый это логика получения продукта.Так как он также используется в предыдущей проверке, то он будет продолжать передаваться по параметрам после предыдущего приобретения, поэтому нет необходимости читать библиотеку или позвонить в сервис здесь для получения;
Во-вторых, необходимо получить данные существующей корзины текущего пользователя, а затем добавить добавленный продукт. Это аналогичная операция слияния.Оказывается, что товар существует, что равносильно прибавлению единицы к количеству, необходимо обратить внимание, имеет ли этот товар отношения родитель-потомок с существующим товаром, и является ли он можно изменить какое-либо правило активности после его добавления, например: изначально купили 2. Один получит 1 подарок, а теперь добавляется еще один, чтобы стало 3, и будут отправлены 2 подарка;
Примечание. Добавление здесь не для прямого изменения количества в корзине, это может быть добавление и добавление непосредственно в список и страницу сведений.
Проверив данные объединенной корзины покупок и подтвердив свое согласие в маркетинговой кампании, они будут напрямую записаны обратно в хранилище.
Объединить корзину
Почему существует операция объединения корзин? Поскольку обычной электронной коммерции разрешено работать в качестве туриста, поэтому, когда пользователь входит в систему, их необходимо объединить.
Логика объединения многих частей здесь — это логика, которую можно повторно использовать при добавлении в корзину. Например, объединенные данные нужно проверить на законность, а затем перезаписать обратно в хранилище. Таким образом, вы можете увидеть корреляцию здесь. Подход к дизайну должен быть несколько общим.
список корзины покупок
Список корзины покупок Это очень важный интерфейс, в принципе, интерфейс корзины покупок будет двух типов: простая версия и полная версия;
Краткая версия интерфейса списка в основном используется для получения простой информации, такой как верхний правый угол домашней страницы ПК; полная версия используется в списке корзины покупок.
В реальной реализации корзина — это не просто интерфейс для чтения. Потому что все мы знаем, что и информация о продуктах, и информация о событиях постоянно меняются. Следовательно, каждый интерфейс чтения должен проверять достоверность данных в текущей корзине покупок, а затем перезаписывать исходные сохраненные данные после обнаружения несоответствий.
Также есть некоторые практики, которые проверяют достоверность данных на каждом интерфейсе, я предлагаю, чтобы из соображений производительности некоторые интерфейсы могли быть правильно проверены, а полная проверка могла быть выполнена при получении списка. Например, при добавлении интерфейса я буду проверять легитимность только тех товаров, которые добавляю, и никогда не буду проверять всю корзину. Поскольку операция списка обычно вызывается после операции, проверка также будет выполняться в это время, и две операции повторяются, поэтому берется только последняя.
поселок
Урегулирование состоит из двух частей, детали расчетной страницы и представления заказа. Можно сказать, что страницу оформления заказа можно назвать пакетом в списке корзины покупок, потому что самая большая разница между страницей оформления заказа и страницей со списком заключается в том, что пользователю необходимо выбрать адрес доставки (для виртуальных товаров). Следовательно, при разработке интерфейса списка корзины покупок мы должны учитывать достаточную общность.
Еще одна вещь, которую нужно обратить внимание на вот: купить немедленно, мы также реализуем ее через интерфейс страницы расчетной страницы, но интерфейс добавления фактически называется внутренне, чтобы добавить продукт в корзину для покупок; Во-первых, это добавление операции это сделано внутри сервиса, и сервисный звонок не должен воспринимать существование этой операции соединения; Во-вторых, ключ корзины в Redis не зависит от обычной корзины для покупок, в противном случае муфта Из двух продуктов очень трудно эксплуатировать и обрабатывать; последняя покупка покупок, которая сразу должна учитывать, что когда учетная запись вошел в систему с несколькими клеммами, данные друг друга не могут влиять на друг друга. Здесь UUID каждого терминала Может использоваться в качестве маркировки корзины, чтобы избежать этой ситуации.
Последним шагом в корзине покупок является создание заказа.Самый важный шаг на этом этапе – блокировка корзины покупок, чтобы избежать подделки данных в процессе отправки.И еще один момент, распределенный код блокировки Redis, написанный многими людьми, имеет дефекты. Обратите внимание на проблему атомарности, многие из этих статей в Интернете не будут их повторять.
После успешной блокировки у нас есть множество методов.Один из них - начать писать таблицу в соответствии с данными, связанными с организацией БД.Это подходит для требований к объему малого бизнеса, таких как объем заказов в секунду. не превышать 2000K, тогда, если ваша система Что делать, если требования параллелизма очень высоки?
На самом деле, это также очень просто, одно из трех волшебных орудий высокой производительности: асинхронность; когда мы отправляем, мы напрямую записываем моментальный снимок данных в MQ, а затем потребляем и обрабатываем асинхронно, что может повысить производительность обработки. контролируя количество потребителей. Хотя производительность этого метода улучшится, его сложность также возрастет.Вы должны выбрать в соответствии с вашей реальной ситуацией.
Что касается проектирования бизнес-архитектуры, то это конец, давайте посмотрим на системную архитектуру.
структура системы
Структура системы в основном включает в себя способ отображения бизнес-архитектуры и описание выходных соответствующих входных и выходных параметров. Поскольку вход и выход определяются для соответствующих бизнесов, и здесь нет никаких сложностей, здесь мы говорим только о том, как сопоставить бизнес-архитектуру с системной архитектурой, а также о выборе основной структуры данных Redis и проектировании структуры данных хранения в Архитектура системы.
Структура кода
Следующий каталог кодов соответствуетGolang
разрабатывать. Давайте посмотрим, как сопоставить приведенную выше бизнес-архитектуру с уровнем кода.
├── addproducts.go
├── cartlist.go
├── mergecart.go
├── entity
│ ├── cart
│ │ ├── add.go
│ │ ├── cart.go
│ │ └── list.go
│ ├── order
│ │ ├── checkout.go
│ │ ├── order.go
│ │ └── submit.go
│ └── precart
├── event
│ └── sendorder.go
├── facade
│ ├── activity.go
│ └── product.go
└── repo
внешний слой имеетentity
,event
,facade
,repo
Эти четыре каталога имеют следующие обязанности:
entity: Сохраняются три сущности в поле покупок, которые мы проанализировали ранее, все основные операции выполняются над этими тремя сущностями;
event: Это используется для обработки сгенерированных событий.Например, если мы отправляем заказ асинхронным способом, каталог должен делать то, как отправлять данные в MQ;
facade: Для чего этот каталог? Это в основном связано с тем, что наши услуги также должны полагаться на такие услуги, как товары и маркетинговая деятельность, поэтому мы не должны вызывать это непосредственно в организации, потому что могут быть изменения в третьей стороне, или может быть увеличение или уменьшение.Здесь мы выполните следующую простую инкапсуляцию (режим фасада в режиме проектирования);
repo: этот каталог в некоторой степени можно понимать какModel
Слой, во всей доменной службе, если речь идет о сохранении, это делается через него.
Последние несколько файлов на внешнем уровне — это предоставляемые нами доменные службы, которые вызываются прикладным уровнем.
Чтобы обеспечить компактность контента, я отказался от каталожного представления микросервисов целиком, а представил доменные сервисы отдельно, а всю системную архитектуру микросервисов представлю позже в отдельном документе.
Благодаря вышеуказанному разделению мы достигли двух целей:
-
Структура анализа бизнес-архитектуры отображена в коде системы, и они отражают друг друга. Самым большим преимуществом этого является обеспечение согласованности дизайна и кода, и вы узнаете, где находится соответствующий код после прочтения документа;
-
Соответствующие задачи каждого каталога разделены, более связны, их легче развивать и поддерживать.
Хранилище Redis
Глядя на это сейчас, мы выбираем Redis в качестве хранилища данных о покупках.Нам нужно решить две проблемы.Во-первых, какие данные нам нужно хранить? Во-вторых, какую структуру мы используем для ее хранения?
Многие люди, которые пишут корзины покупок в Интернете, сохраняют только один идентификатор продукта, и в реальных сценариях трудно удовлетворить спрос. Подумайте об этом, как идентификатор продукта запоминает раздачу, выбранную пользователем? Последнее действие, выбранное пользователем? И канал покупки товара?
Для более общего сценария привожу ссылочную структуру:
// 购物车数据
type ShoppingData struct {
Item []*Item `json:"item"`
UpdateTime int64 `json:"update_time"`
Version int32 `json:"version"`
}
// 单个商品item元素
type Item struct {
ItemId string `json:"item_id"`
ParentItemId string `json:"parent_item_id,omitempty"` // 绑定的父item id
OrderId string `json:"order_id,omitempty"` // 绑定的订单号
Sku int64 `json:"sku"`
Spu int64 `json:"spu"`
Channel string `json:"channel"`
Num int32 `json:"num"`
Status int32 `json:"status"`
TTL int32 `json:"ttl"` // 有效时间
SalePrice float64 `json:"sale_price"` // 记录加车时候的销售价格
SpecialPrice float64 `json:"special_price,omitempty"` // 指定价格加购物车
PostFree bool `json:"post_free,omitempty"` // 是否免邮
Activities []*ItemActivity `json:"activities,omitempty"` // 参加的活动记录
AddTime int64 `json:"add_time"`
UpdateTime int64 `json:"update_time"`
}
// 活动
type ItemActivity struct {
ActID string `json:"act_id"`
ActType string `json:"act_type"`
ActTitle string `json:"act_title"`
}
Сосредоточьтесь на этомItem
эта структура,item_id
Это поле является единственным тегом для пометки товара в корзине, потому что, как мы уже говорили ранее, один и тот же артикул будет двумя разными товарами в корзине из-за разных каналов;parent_item_id
Поле используется для обозначения отношения родитель-потомок.Здесь возможная древовидная структура преобразуется в последовательную структуру.Будь то родительский продукт или дочерний продукт, мы используем последовательное хранилище, а затем используем это поле для связи;некоторые студенты могут быть удивлены, почему хранится идентификатор порядка поля? Каждый обращает внимание на свои повседневные дела, такие как: другой заказ, предварительная продажа депозита и т. д. Это должно быть связано с заказом, будь то проверка квалификации или статистика данных. Остальные поля являются очень обычными полями, поэтому они не будут вводиться по одному;
Тип поля, вы можете изменить его в соответствии с вашими потребностями.
Далее, как выбрать структуру хранения Redis, обычно используется Redis.Hash Table、集合、有序集合、链表、字符串
Пять, мы будем анализировать один за другим.
Прежде всего, корзина для покупок должна иметь ключ для отмены, к которому принадлежит пользователь корзина для покупок. Чтобы упростить, наше ключевое предположение:uid:cart_type
.
Давайте сначала посмотрим, используем ли мыHash Table
; Когда мы его добавляем, нам нужно использовать следующие команды:HSET uid:cart_type sku ShoppingData
; Кажется, нет проблем, мы можем быстро найти товар по артикулу, а затем внести соответствующие изменения и т. д., но учтите, что ShoppingData — это строка json, если у пользователя много товаров в корзине, мы используемHGETALL uid:cart_type
Полученная временная сложность составляет O(n), а затем код необходимо десериализовать один за другим, что составляет сложность O(n).
Если вы используете集合
, также будут сталкиваться с подобными проблемами, каждая корзина рассматривается как коллекция, каждый элемент в коллекции — это ShoppingData, и код все равно нужно десериализовать по одному (десериализация — это стоимость), про упорядоченную коллекцию и связанный список не анализируется, вы можете попробовать решить проблему в соответствии с приведенными выше идеями.
Похоже, у нас нет другого выбора, кроме как использоватьString
, тогда давайте посмотримString
Как выглядит подгонка. во-первыхSET uid:cart_type ShoppingDataArr
; Мы сериализуем все данные корзины покупок в хранилище строк, временная сложность каждого извлечения составляет O(1), а сериализация и десериализация требуется только один раз. Кажется, это очень хороший выбор. Но есть несколько моментов, о которых следует помнить при его использовании.
- Одно значение не может быть слишком большим, иначе возникнет большая ключевая проблема, поэтому у общей корзины есть верхний предел, например, количество товаров, которое нельзя превышать;
- Операционные характеристики Redis были улучшены, но код неудобна при изменении одного элемента. Вы должны прочитать их каждый раз, а затем найти соответствующий элемент для изменения; здесь мы можем прочитать данные от Redis и хранить его в памяти , Построить Hashtable, чтобы уменьшить сложность каждого обход;
Существует также множество структур данных Redis, используемых в сочетании для сохранения данных корзины покупок в Интернете, но это, несомненно, увеличивает нагрузку на сеть.По сравнению со String это наиболее экономичный и рентабельный вариант.
Суммировать
На этом реализация дизайна корзины завершена, а дизайн таблицы заказов будет размещен отдельно в модуле заказов.
Для всего сервиса корзины покупок, хотя и нет подробно написанного конкретного интерфейса, но на данном этапе анализа я считаю, что каждый имеет пробел в своем сердце и может реализовать его в сочетании со своим бизнесом.
В статье есть очень интересные места.Предлагаю сделать это самостоятельно.Если будут вопросы,можем связаться в любое время.
- Адаптированная модель цепочки ответственности
- Распределенные блокировки транзакций Redis для
Следующий шаг, наконец, к дизайну части заказа, я надеюсь, что вы продолжите обращать на нас внимание.
Личный общедоступный номер: dayuTalk
Контактный адрес электронной почты: dayugog@gmail.com
Гитхаб:github.com/helei112g