初识
开源地址:https://github.com/i-love-flamingo/flamingo-commerce
- 首先我们看看效果
官方示例:https://demoshop.flamingo.me
我部署的示例:http://www.itfan.top:3210 - 尝试自己部署
提供了DEMO,地址:https://github.com/i-love-flamingo/commerce-demo-carotene - 我部署的过程(略显坎坷)
- 踩过的坑:
- 首先我使用阿里云服务器(国内节点),搭建环境,包括golang、nodejs(当前最新版);部署的过程中发现版本不兼容,回退go(v1.13.6)、node(v12.16.3)
- 报错 Get https://accounts.google.com/.well-known/openid-configuration: dial tcp 172.217.27.141:443: i/o timeout 这里发现oauth认证使用的是google,我是国内的节点,无法访问,只能vpn解决了,借了mac,上了vpn一顿环境搭建,最后发现还是 timeout
- MAC部署,一样超时,浏览器访问google认证正常,查阅资料有说是google的秘钥认证失败后也不会给结果,同样是超时效果,然后去配了自己的秘钥,还是超时。最后发现mac终端是不走这个代理的,用了各种办法,无果;
- 直接上国外的节点,我的是香港的节点,20分钟搞定
- 总结,网络问题,奔溃
- 下列指令来自remark:
# clone the repo: git clone https://github.com/i-love-flamingo/commerce-demo- carotene.git cd commerce-demo-carotene # Download the test catalog - you only need to do it once - it includes products.csv and images: make download-product-data # Prepare translation files: make translation # Build the flamingo-carotene bases templates: make frontend-build # Run flamingo with flamingo-commerce make serve
- 踩过的坑:
- 整体架构
进入正题,项目采用前后端分离,各司其职,后端是基于commerce,
commerce集成各个模块(模块边界采用DDD领域驱动设计思想),每个模块又基于flamingo框架进行具体的设计开发,系统间的交互采用了六边形的架构思想,通过端口与适配器达到一个可插拔的灵活性;首先我们看下flamingo框架做了什么事;
Flamingo框架特性
这里官网提供了demo 地址:git clone git@github.com:i-love-flamingo/example-helloworld.git
-
依赖注入(Dingo):用在端口 + 适配器、扩展点,上下文的准确映射
injector.Bind().To();
config:配置信息的读取、解析、映射封装
even:事件的出发与监听
-
session:支持多种存储(通过配置中心注入)
Healthcheck:系统指标监控
-
router:路由规则(支持配置 + 代码)
-
cache:响应数据、基础数据缓存
-
template:数据对view渲染,该框架集成了bug,通过
-
SSO:支持单点登录
security:提供了功能权限、CSRF
Logger trace :日志最终
form ->controller : 通过将数据封装到FormHandlerFactory注入到控制层 获取数据
GraphQL :自动生成,映射数据库,提供数据操作;
flamingo-commerce模块简绍
- 项目采用DDD(领域驱动设计)的设计思想,将整个电商业务领域分析和建模,划分为各个子领域,再将各个子域分层处理,提供了一套完整的默认电商实现
下面我们具体分析DDD在项目中如何落地的
- 整个电商系统是一个领域,项目业务将这整个领域细分为产品子域、客户子域、价格子域、订单子域、付款子域、结账子域、交货子域等,通过分析边界,定义了各个子域的界限上下文,通过分析与建模将各个子域领域模型抽象出来,通过DDD的分层思想,将各个领域服务分层
-
下面具体看下car项目的分层
- application:包含了购物车的缓存、获取、修改操作,主要协调组织了业务的流程,当前模块与其模块比如:consumerService、userService进行一个交互
- domain:主要提现业务的概念、状态、规则,包含以下内容
实体:Car;值对象:Address;聚合:Delivery;工厂:DeliveryInfoBuilder;
car:其中包含了觉和信息,如Car(// Cart Value Object (immutable data - because the cartservice is responsible to return a cart).)是一个值对象,其中包含了对送货的操作、对购物车的操作、对总价计算操作,
属性 + 行为- infrastructure:提供了基于内存的购物车管理、email的集成
这里可以重代码中看到框架的灵活
func (m *Module) Inject(routerRegistry *web.RouterRegistry,config *struct {
UseInMemoryCart bool `inject:"config:commerce.cart.useInMemoryCartServiceAdapters,optional"`
EnableCartCache bool `inject:"config:commerce.cart.enableCartCache,optional"`
UseEmailAdapter bool `inject:"config:commerce.cart.useEmailPlaceOrderAdapter,optional"`
},) {
m.routerRegistry = routerRegistry
if config != nil {
m.useInMemoryCart = config.UseInMemoryCart
m.enableCartCache = config.EnableCartCache
m.useEmailAdapter = config.UseEmailAdapter
}
if m.useInMemoryCart {
injector.Bind((*infrastructure.CartStorage)(nil)).To(infrastructure.InMemoryCartStorage{}).AsEagerSingleton()
injector.Bind((*infrastructure.GiftCardHandler)(nil)).To(infrastructure.DefaultGiftCardHandler{})
injector.Bind((*infrastructure.VoucherHandler)(nil)).To(infrastructure.DefaultVoucherHandler{})
injector.Bind((*cart.GuestCartService)(nil)).To(infrastructure.InMemoryGuestCartService{})
injector.Bind((*cart.CustomerCartService)(nil)).To(infrastructure.InMemoryCustomerCartService{})
}
配置指定,通过依赖注入到容器,这里主要就是系统提了默认的数据存储方式,里面是数据的一个存储细节比如增、删、改、查
- interfaces:通过Form将请求数据封装,Controller接收后获取数据,并渲染前端页面;
主要的逻辑 :
1. 从*web.Request 中获取数据等一系列操作,底层看框架提供了通用的方法
func (p *DeliveryFormService) GetFormData(ctx context.Context, req *web.Request) (interface{}, error)
2. 注入service,并调用相应业务
controller:func (cc *CartViewController) Inject(
applicationCartService *application.CartService,
applicationCartReceiverService *application.CartReceiverService,
3. 在grgraphQL中提供查询统计方法
func (cs CartSummary) SumGrandTotalWithGiftCards() *domain.Price {
sum, err := cs.cart.SumGrandTotalWithGiftCards()
if err != nil {
return nil
}
return &sum
}
- 总结,通过清晰的领域定义,我们可以很清晰的看到购物车模块的设计与每一层的作用,体会到DDD设计的可测试、可维护性、灵活性、可扩展性
在项目中六边形架构是如何落地实现的
- 首先总结六边形架构的优点
优点:使用六边形架构开发的软件与通道独立,因此能支持多通道
易于置换入站和出站整合点
测试软件变得更简单,因为可以很容易地模拟集成点 - 通俗讲,该架构就是为了处理系统交互的,通过内部开通单端口,外部实现端口的适配器进行交互,这样我的核心服务不需要变动,可以支持多种协议或组件,做到灵活可插拔性,同时也为测试提供了方便
-
在项目中的实现
cartServicePorts提供了端口,其中包括GuestCartService、CustomerCartService、ModifyBehaviour、、、等系列端口
我们就拿GuestCartService端口来说,
这里是定义的一些借口方法,外部实现这些方法通过adapter,
看下系统如何实现的
通过Adaptter 实现了对数据的内存储 - 总结上面的实现: 这里很清晰的感觉到,模块不会关心你的数据是如何存储的,我给你开了端口,你去实现,如何存储是外部的时,这样就可以兼容通道
总结
这里我主要介绍了项目的几个点,基础框架依赖flamingo,flamingo提供了通用域的实现比如配置、依赖注入、安全、缓存与前前端的一个模板渲染,在此基础上项目只需要关心业务,整个项目采用DDD(领域驱动设计)的架构思想,完成领域的拆分,各个模块依赖降到最低,通过对模块的分层,将各个层的职责单一化;同时结合六边形架构,解耦了模块间的依赖,通过端口 + 适配器的模式,实现多通道、多协议的支持,使项目扩展性更加强
有疑问加站长微信联系(非本文作者)