领域驱动实战-支付系统
在Airwallex,领域驱动设计(DDD)方法被用来指导我们的工袜族程师如何对复杂的业务问题和系统设计建模。在这篇博客中,我们提供了一个全面的工作流,我们使用DDD模式进行建模,然后对支付系统进行落地。
全球支付系统是复杂和不断变化的,涉及从订单、欺诈、通知、与各种支付方式的集成到清算和结算等广泛的板块。
在处理复杂的系统时,大多数开发人员可能会遇到一些一致的问题:
在Airwallex,领域驱动设计(DDD)方法被用来指导我们的工程师如何解决复杂的业务问题和系统建模。
然而,DDD只是各种模式的集合,很难将其应用于系统设计。在这篇博客中,我们提供了一个全面的工作流程,介绍了我们是如何在Airwallex应用领域驱动设计的,从中得到的经验教训,以及我们接下来要做什么。
领域驱动设计(由Eric Evans提出)是一组帮助基于业务领域的底层模型设计软件系统的思想、原则和模式。DDD有两个不同的空间,问题空间和解决方案空间。
在问题空间中,您使用战略模式定义系统的顶层的系统层次,这些战略模式关注域、子域和通用语言的分析。
在解决方案空间中,采用战术模式来提供一组设计模式,您可以使用这些设计模式创建领域模型。这些模式包括有界上下文、上下文映射、实体、聚合、领域事件、领域服务、应用程序服务和基础设施。这些战术模式将帮助您设计既松散耦合又具有内聚性的微服务。
下面是一个常见的案例:
一位顾客想在该商家的网站上购买一件价格为10美元的t恤。顾客可以用多种支付方式来支付这件t恤,比如Visa卡或微信钱包。在客户支付后,商家会从支付网关铅薯收到一个通知,显示客户的支付已经成功。然后,商家可以在Airwallex webapp中查看支付细节,包括购买价格、商家费用以及资金将何时进入Airwallex Global Account钱包。
下面是分析结果。
支付系统
付款处理:商家可以通过各种付款方式接受客户的付款。
金融:清算和解决商家的付款资金。
付款意向:商家创建的订单的价格,产品,客户等。
付款尝试:商家创建的交易以获得订单的客户。
付款方式:客户支付产品的方式。
付款结算:付款之 后钱进入商家钱包。
付款视图:汇总付款详细信息视图,包含与一笔付款相关的所有数据。
有界上下文(BC)限定了域模型的范围。根据对问题空间的分析结果,我们可以定义以下边界上下文:
支付网关: API网关,为商家提供restful API来创建或查看支付。
支付核心模块: 支付意图,方法资源管理。
支付适配器: 与一个外部的PSP集成,例如微信,支槐好者付宝,Visa,万事达等。
支付结算: 计算并结算商户每次支付的原则和费用。支付融合:支付明细汇总视图。
下面是生成的上下文映射的一个示例:
从上面分析的场景和通用语言中,我们可以确定以下聚合、实体、值对象和域事件:
根据我们的经验,领域服务为单个聚合使用业务逻辑服务,遵循单一责任。通常,我们将封装领域仓储、聚合修改和在领域服务中发布的领域事件。以PaymentAttemptExecutorService为例:
领域事件可以使系统更具有可扩展性,并避免任何耦合,且一个聚合不应该决定其他聚合应该做什么。
例如,当PaymentCaptureCommand命令将支付状态更改为已支付时,会发出领域事件PaymentAttemptCapturedEvent。在PaymentAttemptCapturedEvent的领域事件处理程序(EventHandler)中,我们可以在该业务逻辑上加上你想要的逻辑。例如,通知支付聚合有界上下文更新支付详情,通知支付结算有界上下文计算结算金额和费用。
在DDD模式中,基础设施层作用于将核心业务领域与技术实现细节分开。该层通常采用ACL (anti - corruption-layer)模式。以领域仓储为例:
领域仓储只定义接口功能,但实现细节应该隐藏在基础设施层中,细节上你可以使用PostgreSQL或MongoDB来保存数据。例如,在基础设施层中,PaymentAttemptPgRepository是基于PostgreSQL的特定实现,而toPO是一个转换器,用于将域对象PaymentAttempt转换为持久化对象。
现在,我们已经为支付系统定义了一组有界上下文,并在每个有界上下文中标识了一组实体、聚合和领域事件服务。下一步是从域模型到应用程序微服务设计。这里,我们选择将一个有边界的上下文映射到一个微服务。
采用DDD可以提供许多好处,例如,在所有团队之间进行清晰的沟通,以及在设计系统时使用成熟的模式来管理复杂性并提供更好的可伸缩性。
使用通用语言,我们可以实现更多的自描述类名和函数名。
使用聚合模式,我们可以实现清晰的边界和单一的职责。
使用领域事件模式,我们可以分割核心业务流程,减少聚合之间的耦合。
通过基础设施层和ACL模式,我们可以将核心业务领域模型与技术实现细节分离开来。通过限定上下文模式,我们可以派生出潜在的微服务候选对象。
在实践中应用DDD时,我们想要分享一些挑战和经验教训:
原文地址:
vs2013的语言库与vc++的语言库的不同有哪些?
不同的地方很多,具体你可以看msdn上的vs2013的对于C++部分的改进。我想你的VC++指的应该是VC6.0。相比来说VC6.0太老了。在VS2013里使用的是最新的标准,也有好多新的特性,也更安全。
本文档介绍 Visual Studio 2013 中的 Visual C++ 中新增和增强的功能。
有关 Visual Studio 2013 中其他附加内容的信息,请参见 Visual Studio 2013 中的新增功能。
改进谨掘的 ISO C/闭做C++ 标准支持
编译器
支持以下 ISO C++11 语言功能:
函数模板的默认模板参数。
委托构造函数
显式转换运算符。
初始值设定项列表和统一初始化。
原始字符串文本。
可变参数模板。
别名模板。
已删除的函数。
非静态数据成员初始值设定项 (NSDMI)。
默认的函数。 *
支持以下 ISO C99 语言功能:
_Bool
复合文本。
指定的初始值设定项。
组合带有代码的声明。
字符串文本转换为可修改的值可通过使用新编译器选项 /Zc:strictStrings 禁用。 在 C++98 中,已弃用从字符串文本转换至 char *(和将宽字符串文本转换为 wchar_t *)。 在 C++11 中,已将转换完全移除。 虽然编译器可以严格遵循该标准,但提供了 /Zc:strictStrings 选项,以便你控制转换。 默认情况下,该选项是关闭的。 注意,当你在调试模式下使用此选项,STL 将无法编译。
rvalue/lvalue 引用转换。通过 rvalue 引用,C++11 可清晰地区分 lvalue 和 rvalue。 过去,在特定强制转换方案中,Visual C++ 编译器轿晌衡不提供此功能。 已添加新编译器选项(/Zc:rvalueCast),以使编译器与 C++ 语言的工作文件相符,(请参见第 5.4 节,[expr.cast]/1)。
未指定选项时,该默认行为与 Visual Studio 2012 中的相同。
说明
* 默认功能下,不支持使用 =default 逐一请求成员移动构造函数和移动赋值运算符。
C99 库
为下列标头中缺少的函数添加了声明和实现:math.h、ctype.h、wctype.h、stdio.h、stdlib.h 和 wchar.h。 同样添加的还有新标头 complex.h、stdbool.h、fenv.h 和 inttypes.h,以及在这些新标头中声明的所有功能的实现。 还有新的 C++ 包装器标头(ccomplex、cfenv、cinttypes、ctgmath),并且更新了许多其他内容(ccomplex、cctype、clocale、cmath、cstdint、cstdio、cstring、cwchar和 cwctype)。 有关更多信息,请参见 Visual Studio 2013 中的 C99 库支持。
标准模板库
支持 C++11 显式转换运算符、初始值设定项列表、范围枚举和 variadic 模板。
现在所有容器都支持 C++11 细化的元素要求。
支持这些 C++14 功能:
“透明运算符函子”less、greater、plus、multiplies 等。
make_uniqueT(args...) 和 make_uniqueT[](n)
cbegin()/cend()、rbegin()/rend() 和 crbegin()/crend() 非成员函数。
atomic 接收多个性能增强。
type_traits 接收主要稳定性和代码修复。
重大更改
对 ISO C/C++ 标准的改进支持可能需要对现有代码进行更改,从而符合 C++11 并在 Visual Studio 2013 中的 Visual C++ 中正确编译。 有关更多信息,请参见Visual C++ 中的重大更改。
有关新的 C++11/14 语言和 STL 功能的详细信息,请参阅 C++11 功能(现代 C++) 和 Visual Studio 2013 中的 C++11/14 STL 功能、修复和重大更改
Visual C++ 库增强功能
C++ REST SDK 已添加。 它具有 REST 服务的现代 C++ 实现。 有关更多信息,请参见 C++ REST SDK。
C++ AMP 纹理支持已改进。 现在包括对 mipmap 和新采样模式的支持。
PPL 任务支持多个计划技术和异步调试。 采用新 API,可为常规结果和异常条件创建 PPL 任务。
C++ 应用程序性能
自动向量化现在可以识别和优化更多 C++ 模式,加快你的代码运行速度。
ARM 平台和 Atom 微型体系结构代码质量增强功能。
__vectorcall 调用约定已添加。 使用 __vectorcall 调用约定来传递向量类型参数,从而使用向量寄存器。
新链接器选项。使用 /Gw(编译器)和 /Gy(汇编)开关,使链接器优化生成精简二进制文件。
C++ AMP 共享内存支持,可减少或消除 CPU 和 GPU 间的数据复制。
按配置优化选项 (PGO) 增强:
通过使用 PGO 实现已优化的应用程序工作集的缩减,从而提高性能。
面向 Windows 应用商店应用开发的新 PGO。
Windows 应用商店应用开发支持
支持值结构中的装箱类型。现在可以使用可以为空的字段(例如与 IBoxint^ 相对的 int)来定义值类型。 这意味着字段可以具有值,或者与 nullptr 相等。
更丰富的异常信息。C++/CX 支持能够在整个应用程序二进制接口 (ABI) 中获取和传播各种异常信息的新 Windows 错误模型;这包括调用堆栈和自定义消息字符串。
Object::ToString() 现在为虚拟。现在可以重写用户定义的 Windows 运行时引用类型中的 ToString。
支持已弃用的 API。公共 Windows 运行时 API 现在可标记为已弃用并可收到一条自定义消息,此消息显示为生成警告并可提供迁移指南。
调试器改进。支持本机/JavaScript 互操作调试、Windows 运行时异常诊断和异步代码调试(windows 运行时和 PPL)。
说明
除本节中介绍的 C++ 特定功能和增强功能外,Visual Studio 中的其他增强功能还可帮助你编写更好的 Windows 应用商店应用。 有关这些功能的详细信息,请参见 Windows 8.1 功能指南。 有关新应用程序模板的详细信息,请参见 Windows 应用商店应用的 C#、VB 和 C++ 项目模板。 有关新平台功能的列表,请参见 Windows 8.1 预览版:新 API 和功能。
诊断增强功能
调试器改进。支持异步调试和“仅我的代码”调试。
代码分析类别。现在可以查看代码分析器的分类输出,帮助你找到并修复代码缺陷。
XAML 诊断。现在可以诊断 XAML 中的 UI 响应和电池使用情况问题。
图像和 GPU 调试改进。
在实际设备上远程捕获和重放。
同步 C++ AMP 和 CPU 调试。
改进的 C++ AMP 运行时诊断。
HLSL 计算着色器跟踪调试。
三维图形增强功能
图像内容管线支持预乘 alpha DDS 格式。
图像编辑器使用内部预乘 alpha 进行呈现,从而避免呈现暗的光晕等项目。
图像和模型编辑器。现在图像编辑器和模型编辑器的“着色器设计器”中支持用户定义的筛选器创建。
IDE 与工作效率
用 C++ 编码时,Visual Studio IDE 的重大改进将有助于提高你的工作效率。
改进的代码格式设置。你可以将多个格式设置应用于 C++ 代码。 使用这些设置,你可以控制大括号和关键字、缩进、间距和自动换行的新行位置。当完成语句和块并且将代码粘贴到文件中时,代码将自动进行格式化。 若要修改格式设置,请在 Visual Studio 的菜单栏上选择“工具”、“选项”,依次展开“文本编辑器”、“C/C++”和“格式设置”节点,然后进行更改。 你还可以使用“快速启动”框来访问这些选项。
大括号完成。现在,C++ 代码会自动完成对应于这些开始字符的结束字符:
{ (大括号)
[ (方括号)
( (括号)
' (单引号)
" (双引号)
附加 C++ 自动完成功能。
添加用于类类型的分号。
完成对原始字符串文本使用括号。
完成多行注释 (/* */)
查找所有引用自动在后台引用显示出文本匹配列表后对其进行解析和筛选。若要禁用引用解析,可以在 Visual Studio 的菜单栏中选择“工具”、“选项”,依次展开“文本编辑器”、“C/C++”和“高级”节点,然后在“引用”下更改“禁用解析”设置。
若要修改大括号完成设置,请在 Visual Studio 的菜单栏上选择“工具”、“选项”,依次展开“文本编辑器”、“C/C++”和“常规”节点,然后进行更改。 你还可以依次展开“文本编辑器”、“所有语言”和“常规”节点,更改所有 Visual Studio 语言的设置。
若要修改特定的 C++ 设置,请在菜单栏上依次选择“工具”、“选项”,依次展开“文本编辑器”、“C/C++”和“高级”节点,然后进行更改。
基于上下文的成员列表筛选。无法访问的成员已从 IntelliSense 成员列表中筛选出来。例如,私有成员不会在成员列表中显示,除非你修改了实现此类型的代码。 当成员列表中处于打开状态时,你可以按 Ctrl+J 移除筛选的一个级别(仅适用于当前成员列表窗口)。 可以再次按 Ctrl+J 移除文本筛选和显示每个成员。
参数帮助滚动。参数帮助工具提示中显示的函数签名现在将根据实际输入参数的数量而改变,而不是只显示一个随机的签名且不根据当前上下文更新。 函数显示在嵌套函数上时,参数也会适当地帮助函数。
切换标头/代码文件。现在,通过使用快捷菜单或键盘快捷方式上的命令,可以在标题及其相应代码文件之间切换。
可调整大小的 C++ 项目属性窗口。
在 C++/CX 和 C++/CLI 中自动生成事件处理程序代码。在键入代码向 C++/CX 或 C++/CLI 代码文件中添加事件处理程序时,编辑器可以自动生成委托实例和事件处理程序定义。 可以自动生成事件处理程序代码时,会显示工具提示窗口。
DPI 识别增强功能。现在,针对应用程序清单文件的 DPI 识别设置支持“每个高 DPI 识别监视器”的设置。
更快的配置切换。对于大型应用程序,切换配置(尤其是后续切换操作)将更快速地执行。
生成时效。
更快生成。许多优化和多核使用率使生成更加快速,对于大型项目来说尤为如此。 引用了 C++ WinMD 的 C++ 应用程序的增量生成也更加快速。
有关 IDE 中其他添加项和增强功能的信息,请参见 Visual Studio 2013 中的新增功能及其引用的其他文章。
javax.servlet.ServletContextEvent这个类的作用是什么?用来处理什么问题?请高手支招!
javax.servlet.ServletContextEvent是用于通知Web应用掘首的servlet上下文更春散老改的事件类。主要就是为了获取更改后的扒升ServletContext进行后续多种操作。
javax.servlet.ServletContextEvent类结构如下:
java.lang.Object
java.util.EventObject
javax.servlet.ServletContextEvent
javax.servlet.ServletContextEvent类只有一个方法getServletContext() ,
public ServletContext getServletContext()返回被改变的ServletContext对象。
举例如下:
public void contextInitialized(ServletContextEvent sce) {
client = new ServletOAuthClient();
//利用ServletContextEvent获取到ServletContext对象
ServletContext context = sce.getServletContext();
configureClient(context);
context.setAttribute(ServletOAuthClient.class.getName(), client);
}
Spring源码9. refreshContext()刷新应用上下文
上一篇 prepareContext()准备应用上下文 中分析了spring容器的准备, 共计执行了如下8步:
准备刷新, 执行了两步:
清空CachingMetadataReaderFactory中的缓存
设置刷新开始事件, 设置closed为false, active为true, 标记容器处于active状态
AbstractApplicationContext中定义胡兆芹了模板方法, refreshBeanFactory和getBeanFactory调用的是GenericApplicationContext中实现的方法
更新this.refreshed字段为true, 表示已经更新了, 然后beanFactory设置serializationId, 最后返回beanFactory
beanFactory是GenericApplicationContext中DefaultListableBeanFactory类型的成员变量, 设置beanFactory, 一共执行了
后续处理各个beanFactory, 当前applicationContext是AnnotationConfigServletWebServerApplicationContext的实例, postProcessBeanFactory执行了三步
进行了两个操作, 首先添加了一个WebApplicationContextServletContextAwareProcessor的Aware Bean处理器, ServletContextAware的子类Bean在实例化过程中, 会被注入猜尘servletContext和servletConfig对象, 然后beanFactory中注册了request和session两个scopes, 注册了几个Autowired依赖类
注册了request, session两个scope, 然后注册ServletRequest, ServletResponse, HttpSession, WebRequest
BeanFactoryPostProcessor是一个接口, 处理beanFactory中所有的bean, 在所有的beanDefinition加载完成之后, BeanFactoryPostProcessor可以对beanDefinition进行属性的修改, 之后再进行bean实例化
BeanDefinitionRegistryPostProcessor是裤毕BeanFactoryPostProcessor的子接口, 定义了postProcessBeanDefinitionRegistry方法, 会在postProcessBeanFactory方法执行之前, 获取bean定义, 并注册到spring容器中
如果beanFactory是BeanDefinitionRegistry的子类, 按优先级处理BeanDefinitionRegistryPostProcessor类型的后置处理器, 最后处理传入的其他类型后置处理器, 处理流程如下:
如果beanFactory不是BeanDefinitionRegistry的子类, 那么直接遍历传入的传入的beanFactoryPostProcessors, 调用元素的postProcessBeanFactory方法
最后处理beanFactory中注册的其他类型的BeanFactoryPostProcessor, 获取bean名称, 维护到postProcessorNames列表中, 之后的处理步骤如下:
ConfigurationClassPostProcessor处理了@Configuration注解, 扫描项目中的BeanDefinition, 这篇文章 详细剖析了ConfigurationClassPostProcessor的源码
BeanPostProcessor是一个接口, Bean后置处理器, 在bean实例化, 之前执行postProcessBeforeInitialization方法, 在bean实例化之后执行postProcessAfterInitialization方法, 实现了对bean实例的增强
beanFactory中获取BeanPostProcessor类型的bean名称, 维护到postProcessorNames数组中, 将BeanPostProcessor列表分为四类:
beanFactory先添加一个BeanPostProcessorChecker类型的BeanPostProcessor, 然后在将各类PostProcessors列表排序, 分别添加到beanFactory的beanPostProcessor列表中, 最后再添加一个ApplicationListenerDetector
先判断容器beanFactory中是否包含messageSource bean定义, 存在的话, 直接获取bean, 如果不存在的话, 那么手工注册一个messageSource单例bean, 然后赋值给this.messageSource
先判断容器beanFactory中是否有applicationEventMulticaster bean定义, 存在的话, 获取bean实例, 不存在的话, 实例化一个SimpleApplicationEventMulticaster, 手工注册一个单例bean, 然后赋值给this.applicationEventMulticaster
AbstractApplicationContext没有实现该方法, 用于通知子类刷新容器
调用父类GenericWebApplicationContext#onRefresh方法, 然后创建webServer, 之后调用父类GenericWebApplicationContext#initPropertySources方法, 将servletContext维护到environment的servletContextInitParams属性中
初始化主题, 可以让页面显示不同的样式
首先将硬编码的ApplicationListener先添加this.applicationEventMulticaster.defaultRetriever.applicationListeners中, 然后将注入的listener bean维护到this.applicationEventMulticaster.defaultRetriever.applicationListenerBeans, 最后处理earlyEvent
beanFactory.preInstantiateSingletons() 源码剖析
什么都没有做
打印@Condition注解评估日志