一个好的架构
- 一套统一的分层,分包,分模块的约束规范。
- 业务逻辑和技术实现分离,整洁架构的核心思想就是要避免业务逻辑的复杂度与技术实现的复杂度混淆在一起,确定业务逻辑与技术实现的边界,从而隔离各自的复杂度,业务逻辑并不关心技术是如何实现的。
- 数据库无关性:业务逻辑不用关心使用 Postgres、Oracle、MongoDB 还是 Mysql。可以方便的在这些数据库中切换,业务逻辑不能依赖于这些数据库。
- UI无关性:核心业务逻辑不关心你使用 Rest API、Dubbo、Grpc还是CLI(command-line interface),业务逻辑不能依赖于这些UI,当你把系统的UI从Web UI改成控制台UI,你并不需要改变任何业务逻辑的代码。
- 应用框架无关性:使用 nodeJS、express还spring boot?你的核心业务逻辑也不关心这些,这样做不会让你的业务逻辑在使用前就有一些强制性的约束。
- 与一切外部无关性:系统的业务逻辑并不需要知道任何外部的结构。
- 可测试性
- 业务逻辑必须能独立测试,不需要依赖UI,数据库,Web服务器或者一些其他的外部条件。
- 集成自研的单元测试框架,解决单元测试的痛点。
- 依赖清晰性
- 依赖关系不与数据流,控制流挂钩,只与层次挂钩,高层次依赖低层次。
- 外层次向内层次依赖,内部不依赖外部,依赖包含代码名称,或类的函数,变量或任何其他命名软件实体。
- 外面层次中使用的数据格式不应被内层次中使用,我们不希望任何外圆的东西会影响内圈层。
DDD整洁架构实践
https://github.com/Sairyss/domain-driven-hexagon
六边形架构与洋葱架构其中心思想类似
纵向DGA架构设计
基础框架设计
DDD
战略设计
/* The DDD Cargo sample application modeled in CML. Note that we split the application into multiple bounded contexts. */
ContextMap AdIndexMap {
contains AdIndexContext,GoodsContext,SellerContext,StockContext,PromotionContext,AddressCenterContext
contains StationContext,BatchStockContext,ForcastSalesVolumeContext,RecallContext,MergeContext,RecommendContext
contains GoodsTeam,StockPlanTeam,SupplyDemandTeam,PromotionTeam,ShareBusinessTerm,StationTerm,SearchRecommendTerm
AdIndexContext [D,ACL]<-[U,OHS,PL] GoodsContext
AdIndexContext [D,ACL]<-[U,OHS,PL] GoodsContext
AdIndexContext [D,ACL]<-[U,OHS,PL] SellerContext
AdIndexContext [D,ACL]<-[U,OHS,PL] StockContext
AdIndexContext [D,ACL]<-[U,OHS,PL] PromotionContext
AdIndexContext [D,ACL]<-[U,OHS,PL] AddressCenterContext
AdIndexContext [D,ACL]<-[U,OHS,PL] StationContext
AdIndexContext [D,ACL]<-[U,OHS,PL] BatchStockContext
AdIndexContext [D,ACL]<-[U,OHS,PL] ForcastSalesVolumeContext
MergeContext [C]<-[S] AdIndexContext
MergeContext [P] <-> [P] RecallContext
RecommendContext [D] <- [OHS,PL] MergeContext
RecommendContext [SK] <-> [SK] MergeContext
//GoodsTeam[U,S]->[D,C]SupplyDemandTeam
//StockPlanTeam[U,S]->[D,C]SupplyDemandTeam
//PromotionTeam[U,S]->[D,C]SupplyDemandTeam
//ShareBusinessTerm[U,S]->[D,C]SupplyDemandTeam
//StationTerm[U,S]->[D,C]SupplyDemandTeam
//SupplyDemandTeam[U,S]->[D,C]SearchRecommendTerm
}
BoundedContext AdIndexContext implements AdIndexDomain
BoundedContext GoodsContext implements GoodsDemain
BoundedContext SellerContext implements SellerDomain
BoundedContext StockContext implements StockDomain
BoundedContext PromotionContext implements PromotionDomain
BoundedContext AddressCenterContext implements AddressCenterDomain
BoundedContext StationContext implements StationDomain
BoundedContext BatchStockContext implements BatchStockDomain
BoundedContext ForcastSalesVolumeContext implements ForcastSalesVolumeDomain
BoundedContext RecallContext implements RecallDomain
BoundedContext MergeContext implements MergeDomain
BoundedContext RecommendContext
/* Team Definitions */
BoundedContext GoodsTeam realizes GoodsContext, SellerContext{
type = TEAM
domainVisionStatement = "供应链 商品研发分组."
}
BoundedContext StockPlanTeam realizes StockContext, BatchStockContext{
type = TEAM
domainVisionStatement = "供应链 库存计划."
}
BoundedContext SupplyDemandTeam realizes ForcastSalesVolumeContext, AdIndexContext,MergeContext,RecallContext{
type = TEAM
domainVisionStatement = "供应链 供需决策."
}
BoundedContext PromotionTeam realizes PromotionContext{
type = TEAM
domainVisionStatement = "营销研发组-用户研发分组."
}
BoundedContext ShareBusinessTerm realizes AddressCenterContext{
type = TEAM
domainVisionStatement = "技术中台组-共享服务分组"
}
BoundedContext StationTerm realizes StationContext{
type = TEAM
domainVisionStatement = "履约研发组-门店研发分组"
}
BoundedContext SearchRecommendTerm realizes RecommendContext{
type = TEAM
domainVisionStatement = "搜索推荐"
}
Domain AdDomain {
Subdomain MergeDomain {
type = CORE_DOMAIN
domainVisionStatement = "广告mergeRerank核心子域"
}
Subdomain RecallDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "召回支撑域."
}
Subdomain AdIndexDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "索引支撑域."
}
}
Domain AdIndexDomain {
Subdomain IndexDomain {
type = CORE_DOMAIN
domainVisionStatement = "广告索引核心子域"
}
Subdomain GoodsDemain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "商品支撑域."
}
Subdomain SellerDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "卖家支撑域."
}
Subdomain StockDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "库存支撑域."
}
Subdomain AddressCenterDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "基础地址支撑域."
}
Subdomain StationDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "门店支撑域."
}
Subdomain PromotionDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "促销支撑域."
}
Subdomain BatchStockDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "批次库存支撑域."
}
Subdomain ForcastSalesVolumeDomain {
type = SUPPORTING_DOMAIN
domainVisionStatement = "预估销量支撑域."
}
}
战术设计
/* The original booking application context */
BoundedContext AdMergeRerankContext {
Module ad {
/**
*
* {com.公司名.组织架构.业务.上下文.*},这样的组织结构能够明确的将一个上下文限定在包的内部。
* import com.company.team.bussiness.lottery.*;//抽奖上下文
* import com.company.team.bussiness.riskcontrol.*;//风控上下文
* import com.company.team.bussiness.counter.*;//计数上下文
* import com.company.team.bussiness.condition.*;//活动准入上下文
* import com.company.team.bussiness.stock.*;//库存上下文
* import com.company.team.bussiness.lottery.domain.valobj.*;//领域对象-值对象
* import com.company.team.bussiness.lottery.domain.entity.*;//领域对象-实体
* import com.company.team.bussiness.lottery.domain.aggregate.*;//领域对象-聚合根
* import com.company.team.bussiness.lottery.service.*;//领域服务
* import com.company.team.bussiness.lottery.repo.*;//领域资源库
* import com.company.team.bussiness.lottery.facade.*;//领域防腐层
*
*/
basePackage = com.company.team.bussiness.ad.merge.rerank.domain
Entity RecoBean {
/**
* sku id .
*/
Integer skuId;
/**
* sku name.
*/
Integer skuName;
}
ValueObject SkuPosition {
Integer position
- RecoBean recoBean
}
ValueObject PositionSpecification{
- List<SkuPosition> fixedPositionMap
/**
* 原始列表的售罄标记
*/
String originSoldOutMark;
- List<RecoBean> soldOutRecoBeanList
def void isSatisfiedBy(List<@RecoBean> mergeResult);
}
ValueObject SkuPositionMarker {
- PositionSpecification positionSpecification
- List<RecoBean> inclinableRecoBeanList
- List<SkuEntity> inclinableAdList
}
ValueObject ScoreStrategy {
def double score(SkuEntity SkuEntity);
}
Aggregate SkuAggregation {
Entity SkuEntity {
aggregateRoot
/**
* sku id
*/
SkuId skuId
Integer originPosition;
Boolean isIncline;
Integer rerankPosition;
Double score;
Double originScore;
Boolean isAd;
Boolean isRecommend;
- Price price
- Category category
- ShelfLife shelfLife
- Promotion promotion
- Stock stock
- BasicInfo basicInfo
Repository SkuRepository {
List<@SkuEntity> findAll(String recallKey);
}
}
ValueObject Price {
/**
* 会员价 .
*/
Integer price;
/**
* VIP价.
*/
Integer vipPrice;
/**
* 成本价.
*/
Integer costPrice;
def double score(double originScore);
}
ValueObject Category {
/**
* 后台一级品类.
*/
Integer firstCid;
/**
* 后台二级品类.
*/
Integer secondCid;
/**
* 后台三级品类.
*/
Integer thirdCid;
/**
* 后台一级品类名称.
*/
String firstCname;
/**
* 后台二级品类名称.
*/
String secondCname;
/**
* 后台三级品类名称.
*/
String thirdCname;
/**
* 前台一级类目ID.
*/
Set<Integer> frontFirstCidSet;
/**
* 前台二级类目ID.
*/
Set<Integer> frontSecondCidSet;
/**
* 前台一级类目名称.
*/
Set<String> frontFirstCnameSet;
/**
* 前台二级类目名称.
*/
Set<String> frontSecondCnameSet;
def double score(double originScore);
}
ValueObject ShelfLife {
/**
* 总货架期(天).
*/
Integer totalShelfLife;
/**
* 允收期(小时).
*/
Integer allowReceiveShelfLife;
/**
* 微仓最小货架期.
*/
Integer microWarehouseMinShelfLife;
/**
* 大仓货架期.
*/
Integer bigWarehouseShelfLife;
/**
* 周围规则.
*/
Integer turnoverRule;
/**
* 超效期周转倍数
*/
private Double overExpiryMulti;
private Double otherOverExpiryMulti;
def double score(double originScore);
}
ValueObject Promotion {
/**
* 促销开始时间
*/
Long startPromotionTime;
/**
* 促销结束时间
*/
Long endPromotionTime;
/**
* 促销类型
*/
Integer promotionType;
/**
* 促销场景
*/
Integer promotionScene;
def double score(double originScore);
}
ValueObject Stock {
/**
* 库存.
*/
Integer stock;
/**
* 批次库存
*/
List<BatchStockBO> batchStocks;
/**
* 总批次库存
*/
Integer batchStockWarning;
/**
* 临期报损库存
*/
Integer warningStock;
def double score(double originScore);
}
ValueObject BasicInfo {
/**
* sku名称.
*/
String skuName;
/**
* 商品上下架状态 2上架,3预下架,4预上架.
*/
Integer skuStatus;
/**
* sku类型 默认为0 1原料商品,2普通商品,3虚拟组套,4虚拟商品.
*/
Integer skuType;
/**
* 商品类型 默认为0 1原料商品,2普通商品,3虚拟组套,4虚拟商品.
*/
Integer itemType;
/**
* 业态 (卖家类型,一个SKU只有一个卖家类型).
*/
Integer sellerType;
/**
* 是否预售 1为预售品,0为否.
*/
Integer isPresale;
Long sellerId;
def double score(double score);
}
Service MergeRankService {
@SkuPositionMarker fixedPositionPrepare(List<@RecoBean> originRecoList,List<@SkuEntity> adSkuEntities,Set<String> batchSkuSet);
@SkuPositionMarker allInclinablePrepare(List<@RecoBean> originRecoList,List<@SkuEntity> adSkuEntities,Set<String> batchSkuSet);
void rank(List<@SkuEntity> adEntries,@ScoreStrategy scoreStrategy);
List<@RecoBean> merge(@SkuPositionMarker skuPositionMarker,List<@SkuEntity> adEntries);
List<@RecoBean> merge(@SkuPositionMarker skuPositionMarker,List<@SkuEntity> adEntries,List<Integer> positions);
List<@RecoBean> shuffler(@SkuPositionMarker skuPositionMarker,List<@RecoBean> sortedResult);
Boolean mergeValid(@SkuPositionMarker skuPositionMarker,List<@RecoBean> sortedResult);
}
}
}
}
#http://bj.91join.com/color.html
digraph G1 {
rankdir=TB;
graph [compound=true]
node [color=black,shape=egg fillcolor="#FFFFFF" style="filled" shape=egg fontcolor="#000000"] //All nodes will this shape and colour
edge [color=black] //All the lines look like this
subgraph cluster_basic {
fillcolor="#FFAB00"
fontcolor="white"
style="filled"
label = "Infrastructure Layer";
SpringBoot[shape=egg]
Dubbo[shape=egg]
#_[color="white" fontcolor="#ffffff"]
ElasticSearchRepositories[shape=egg]
RedisRepositories[shape=egg]
"Data_Mapper DO<->PO"
};
subgraph cluster_domain {
fillcolor="#01939A"
style="filled"
fontcolor="white"
label = "Domain Layer"
Repository_API[shape=egg]
subgraph cluster_ddd{
Entity[shape=egg]
Value_Object[shape=egg]
Domain_Service[shape=egg]
Aggregation[shape=egg]
label = "Domain DDD"
}
};
subgraph cluster_protocol{
fillcolor="#9FEE00"
fontcolor="white"
style="filled"
label = "Protocol Layer"
DTO RpcAPI
}
subgraph cluster_app{
fillcolor="#9FEE00"
fontcolor="white"
style="filled"
label = "Application Layer"
DTO_Assemble
Adapters
subgraph cluster_service{
{
rank="same";Valid;Shuffler;Merge;ReRank;Rank;Filter;Recall;}
Recall->Filter->Rank->ReRank->Merge->Shuffler->Valid
label = "Operation Flow"
}
}
subgraph cluster_commons{
fillcolor="#CD0074"
fontcolor="white"
style="filled"
label = "Commons"
commons[fillcolor="white"
style=filled shape=egg]
}
SpringBoot -> Shuffler[label=" " ltail=cluster_basic lhead=cluster_app]
Shuffler -> Entity [label=" " lhead=cluster_domain ltail=cluster_app];
Shuffler -> RpcAPI[label=" " ltail=cluster_app lhead=cluster_protocol];
Domain_Service->commons[label=" " ltail=cluster_domain];
ElasticSearchRepositories-> Repository_API
[label="DI" fontcolor="#9FEE00" style="dotted"];
RedisRepositories-> Repository_API
[label="DI" style="dotted" fontcolor="#9FEE00"];
}