细节:内部和外部的代码如何组织?
除了上面提到的原则,我们完全可以自由地按照我们的意愿在每个区域内组织代码。
关于业务代码,内部,一个好主意是选择根据业务逻辑组织其模块(或目录)。
要避免的一个组织是按类型对类进行分组。例如“ports”目录,或“repositories”目录(如果使用此模式)或“services”目录。在您的业务代码中考虑100%的业务,包括组织您的模块或目录!理想的情况是能够打开目录或业务逻辑模块,并立即了解您的程序解决的业务问题;而不是只看到“存储库”,“服务”或其他“经理”目录。
另请参阅此主题:
https://medium.com/@msandin/strategies-for-organizing-code-2c9d690b6f33
https://martinfowler.com/bliki/PresentationDomainDataLayering.html
细节:运行时
您如何实例化所有这些以满足运行时依赖性?如果您使用依赖注入框架,则可能不需要问自己这个问题。但我认为要理解六边形体系结构,看看应用程序启动时会发生什么是很有趣的。要做到这一点,至少在本文的时候不要使用依赖注入框架。
例如,如果我们手动实例化一切,我们将如何编写应用程序的入口点:
class Program
{
static void Main(string[] args)
{
// 1. Instantiate right-side adapter ("go outside the hexagon")
IObtainPoems fileAdapter = new PoetryLibraryFileAdapter(@".\\Peoms.txt");
// 2. Instantiate the hexagon
IRequestVerses poetryReader = new PoetryReader(fileAdapter);
// 3. Instantiate the left-side adapter ("I want ask/to go inside")
var consoleAdapter = new ConsoleAdapter(poetryReader);
System.Console.WriteLine("Here is some...");
consoleAdapter.Ask();
System.Console.WriteLine("Type enter to exit...");
System.Console.ReadLine();
}
}
实例化顺序通常是从右到左:
- 首先我们实例化Infrastructure端,这里是fileAdapter,它将读取文件。
- 我们实例化将由应用程序驱动的Domain类,poetryReader在其中通过注入将fileAdapter注入构造函数。
- 安装Application端,consoleAdapter将驱动poetryReader并写入控制台。这里poetryReader通过注入构造函数注入consoleAdapter。
我们说内部不应该依赖于外部。那么为什么我们将来自Infrastructure的代码fileAdapter注入poetryReader,这是来自Domain的代码?
我们可以这样做,因为通过查看模式和代码,除了是PoetryLibraryFileAdapter(基础结构方面)之外,fileAdapter也是继承的IObtainPoems实例。
在实践中,PoetryReader不依赖于PoetryLibraryFileAdapter,而是依赖于IObtainPoems,它在域中定义良好。您可以通过查看其构造函数的签名来检查它。
public PoetryReader(IObtainPoems poetryLibrary)
{
this.poetryLibrary = poetryLibrary;
}
PoetryLibraryFileAdapter和PoetryReader是弱耦合的。
细节:右侧的依赖性反转
fileAdapter依赖于业务的定义(依赖于继承),但在运行时poetryReader可以在实践中控制fileAdapter的实例是依赖倒置的经典案例。
实际上,如果没有IObtainPoems接口,业务代码将依赖于其定义的基础结构,我们希望避免:
该接口允许反转此依赖关系的方向:
除了使业务独立于外部系统之外,右侧的此接口还允许满足着名的 SOLID或Dependency Inversion Principle原则。这个原则说:
- 高级模块不应该依赖于低级模块。两者都必须依赖于抽象。
- 抽象不应该依赖于细节。细节必须取决于抽象。
如果我们没有接口,我们将拥有一个依赖于低级模块(Infrastructure)的高级模块(Domain)。
注意:对于左侧和业务代码之间的交互,依赖性自然是正确的方向。
交互实现的这种差异与应用程序域和域 - 基础架构关系之间的差异有关。提醒:应用程序端驱动域,而基础架构端由域驱动。
细节:为什么左边有接口?
由于Application和Domain之间的依赖关系已经在正确的方向上,因此IRequestVerses接口的作用不是反转依赖关系。
但是,它仍然有兴趣:明确限制应用程序代码和域代码之间的耦合表面。
实际上,PoetryReader类可以有除IRequestVerses接口之外的其他方法。ConsoleAdapter不了解这一点很重要。
它与另一个SOLID原则 - 接口隔离原则一致。
客户不应该被迫依赖他们不使用的方法。
但是,一旦你理解了意图,如果左侧的端口只有一个方法,并且它的实现只有一个方法,如我们的例子,接口真的是必要的吗?在动态语言中,最终将通过duck typing?
我们可以回答一个问题:您的团队对此有何看法?每个人都清楚隔离目标,甚至不需要界面来触发对话吗?这取决于你完全决定。
六角形结构测试
该软件架构的一个重要优点是它有助于测试自动化,这是其原始意图的一部分。
如何从Application端替换一些代码?
在一般情况下,左侧代码的作用可以由测试框架直接扮演。实际上,测试代码可以直接驱动业务逻辑代码。
注意:该图说明了集成测试,因为没有替换正确的部分。它也可以替换,见下文。
如何替换基础设施方面的一些代码?
右边的代码必须由业务驱动。通常,如果要编写单元测试,可以使用模拟或任何其他形式的测试双重替换它,具体取决于您要测试的内容。
达到了目标!
允许应用程序由用户,程序,自动化测试或批处理脚本驱动,并与其可能的执行系统和数据库隔离开发和测试。
小心!这并不妨碍您测试应用程序和基础结构代码,任何值得测试的代码。在这个主题上,我再次向您推荐实践测试金字塔系列。
事实上,通过组合我们替换或不替换的内容,我们可以看到,通过这种架构,我们可以测试我们想要的东西:
- 整个域单独,
- 在Infrastructure端独立地集成Application和Domain
- 在应用程序端独立地集成域和基础结构
-
Web
+关注
关注
2文章
1264浏览量
69524 -
代码
+关注
关注
30文章
4798浏览量
68714
发布评论请先 登录
相关推荐
评论