六角架构学的三个原则和技术2

电子说

1.3w人已加入

描述

细节:内部和外部的代码如何组织?

除了上面提到的原则,我们完全可以自由地按照我们的意愿在每个区域内组织代码。

关于业务代码,内部,一个好主意是选择根据业务逻辑组织其模块(或目录)。

要避免的一个组织是按类型对类进行分组。例如“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();
 }
}

实例化顺序通常是从右到左:

  1. 首先我们实例化Infrastructure端,这里是fileAdapter,它将读取文件。
  2. 我们实例化将由应用程序驱动的Domain类,poetryReader在其中通过注入将fileAdapter注入构造函数。
  3. 安装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接口,业务代码将依赖于其定义的基础结构,我们希望避免:

Web

该接口允许反转此依赖关系的方向:

Web

除了使业务独立于外部系统之外,右侧的此接口还允许满足着名的 SOLID或Dependency Inversion Principle原则。这个原则说:

  1. 高级模块不应该依赖于低级模块。两者都必须依赖于抽象。
  2. 抽象不应该依赖于细节。细节必须取决于抽象。

如果我们没有接口,我们将拥有一个依赖于低级模块(Infrastructure)的高级模块(Domain)。

注意:对于左侧和业务代码之间的交互,依赖性自然是正确的方向。

Web

交互实现的这种差异与应用程序域和域 - 基础架构关系之间的差异有关。提醒:应用程序端驱动域,而基础架构端由域驱动。

细节:为什么左边有接口?

由于Application和Domain之间的依赖关系已经在正确的方向上,因此IRequestVerses接口的作用不是反转依赖关系。

但是,它仍然有兴趣:明确限制应用程序代码和域代码之间的耦合表面。

实际上,PoetryReader类可以有除IRequestVerses接口之外的其他方法。ConsoleAdapter不了解这一点很重要。

它与另一个SOLID原则 - 接口隔离原则一致。

客户不应该被迫依赖他们不使用的方法。

Web

但是,一旦你理解了意图,如果左侧的端口只有一个方法,并且它的实现只有一个方法,如我们的例子,接口真的是必要的吗?在动态语言中,最终将通过duck typing?

我们可以回答一个问题:您的团队对此有何看法?每个人都清楚隔离目标,甚至不需要界面来触发对话吗?这取决于你完全决定。

六角形结构测试

该软件架构的一个重要优点是它有助于测试自动化,这是其原始意图的一部分。

如何从Application端替换一些代码?

在一般情况下,左侧代码的作用可以由测试框架直接扮演。实际上,测试代码可以直接驱动业务逻辑代码。

Web

注意:该图说明了集成测试,因为没有替换正确的部分。它也可以替换,见下文。

如何替换基础设施方面的一些代码?

右边的代码必须由业务驱动。通常,如果要编写单元测试,可以使用模拟或任何其他形式的测试双重替换它,具体取决于您要测试的内容。

达到了目标!

允许应用程序由用户,程序,自动化测试或批处理脚本驱动,并与其可能的执行系统和数据库隔离开发和测试。

小心!这并不妨碍您测试应用程序和基础结构代码,任何值得测试的代码。在这个主题上,我再次向您推荐实践测试金字塔系列。

事实上,通过组合我们替换或不替换的内容,我们可以看到,通过这种架构,我们可以测试我们想要的东西:

  • 整个域单独,
  • 在Infrastructure端独立地集成Application和Domain
  • 在应用程序端独立地集成域和基础结构
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分