人类是按类别思考的,我们的代码也应该反映出这一点。

首先,我并没有提出 领域 这个术语——我是从流行的编程范例 DDD,或 领域驱动设计 中得到的。根据牛津词典,领域 可以描述为“一个特定的活动或知识领域”。

虽然我对“域”一词的使用与 DDD 中使用时的含义并不完全相同,但有几个相似之处。如果你熟悉 DDD,你会在本书中发现这些相似之处。我尽力在相关的时候提到任何重叠和差异。

所以,域,你也可以称它们为 组 groups模块 modules;有些人称它们为 服务 services。无论你喜欢哪个名称,域都描述了一组您试图解决的业务问题。

等等……我意识到我刚刚在这本书中使用了第一个“企业”术语:“商业问题”。在阅读这本书的过程中,您会注意到我尽力避开了理论、上层管理和业务方面的内容。我自己就是一名开发人员,我更喜欢实用性。因此,另一个更简单的名字将是“项目”。

让我们举一个例子:一个管理酒店预订的应用程序。它必须管理客户、预订、发票、酒店库存等。

现代的 web 框架教你采用一组相关概念,并将其分割到代码库中的多个位置:控制器与控制器一起,模型与模型一起;你懂的。

是否有客户告诉过你,“现在处理所有的控制器”,或者“花一些时间在模型目录下”?没有——他们要求你处理发票、客户管理或预订功能。

这些组就是我所说的域。它们旨在将项目中属于一起的概念组合在一起。一开始这可能看起来微不足道,但比你想象的要复杂的多。这就是为什么这本书的一部分将集中在一组规则和实践上,以保持你的代码井然有序。

很明显,我没有数学公式可以给你,几乎所有的事情都取决于你所从事的具体项目。所以不要把这本书看作是给了一套固定的规则。相反,你可以把它看作是给你一个想法的集合,你可以用它来构建,不管你喜欢什么。

这是一个学习的机会,而不仅仅是你遇到任何问题时都能给出的一个解决方案。

域和应用程序

如果我们把想法都组合在一起,很明显问题就出现了:我们要走多远?例如,您可以将发票相关的所有内容组合在一起:模型、控制器、资源、验证规则、任务…

这就提出了经典 HTTP 应用程序中的一个问题:控制器和模型之间通常没有一对一的映射。诚然,在 REST api 和大多数经典的 CRUD 控制器中可能存在严格的一对一映射,但不幸的是,这些规则的例外会给我们带来困难。例如,发票不是单独处理的,它们需要发送给客户,需要预订来开具发票,等等。

这就是为什么我们需要进一步区分什么是域代码,什么不是域代码。

一方面,领域代表了所有的业务逻辑;另一方面,我们有使用(消费)该域的代码来将其与框架集成,并将其公开给最终用户。应用程序以用户友好的方式为最终用户使用和操作领域提供了基础设施。

在实践中

那么,这在实践中是什么样子的呢?域将包含模型、查询生成器、域事件、验证规则等类;我们将深入研究所有这些概念。

应用层将容纳一个或多个应用程序。每个应用程序都可以看作是一个独立的应用程序,它被允许使用所有的域。一般来说,应用程序不相互通信。

一个示例可以是标准的 HTTP 管理面板,另一个示例可以是 REST API。我也喜欢把Laravel 的 artisan 控制台看作是自己的应用。

作为高级概述,一个面向领域项目的文件夹结构,可能如下所示:

每个业务概念都有一个特定的域文件夹
app/Domain/Invoices/
    ├── Actions
    ├── QueryBuilders
    ├── Collections
    ├── DataTransferObjects
    ├── Events
    ├── Exceptions
    ├── Listeners
    ├── Models
    ├── Rules
    └── States

app/Domain/Customers/
    // …

应用层应该是这样的:

HTTP应用程序管理
app/App/Admin/
    ├── Controllers
    ├── Middlewares
    ├── Requests
    ├── Resources
    └── ViewModels

REST API 应用
app/App/Api/
    ├── Controllers
    ├── Middlewares
    ├── Requests
    └── Resources

控制台应用
app/App/Console/
    └── Commands

关于命名空间的话题

你可能已经注意到,上面的示例并没有遵循 Laravel 以 \App 作为单个根名称空间的约定。由于应用程序只是我们项目的一部分,而且可能有好几个,所以使用 \App 作为所有东西的根是没有意义的。

如果你更喜欢接近Laravel的默认结构,你可以这么做。这意味着您将得到像 \App\Domain\App\Api 这样的名称空间。但是你可以自由地做你觉得舒服的事情。

但是,如果想分离根命名空间,你可以通过稍微改变一下 composer.json:

{
    // …

    "autoload" : {
        "psr-4" : {
            "App\\" : "app/App/",
            "Domain\\" : "app/Domain/",
            "Support\\" : "app/Support/"
        }
    }
}

请注意,我还有一个 \Support 根命名空间,暂时你可以将其视为那些不属于任何域的小助手的转储地。

无论使用什么文件夹结构,最重要的是开始以相关业务概念组的方式进行思考,而不是以具有相同技术属性的代码组的方式进行思考。

在每个组、每个域中,都有空间以使代码在这些单独的组中易于使用的方式构造代码。本书的第一部分将仔细研究如何在内部构建域,以及可以使用哪些模式来帮助你保持代码库的可维护性。在此之后,我们将查看应用层,如何准确地使用域,以及如何通过使用示例视图模型改进现有的 Laravel 概念。

其中涉及的内容很多,我希望你能从中学到很多东西,并立即付诸实践。