构建可维护的 PHP 应用:思考数据与思考业务流程
互联网上的大多数教程以及其他正规或非正规教育,都教会开发人员如何用 CRUD 思维构建项目,CRUD 思维高度技术化,专注于数据操作,类似于 Excel 的发明初衷,几十年来一直在做它的工作。
这是可以理解的,因为编程的入门材料需要简单易用。
不要误解我的意思,作为一个起点,内容对初学者来说很好,但当构建真实世界的项目时,它们比学习材料中显示的更复杂。
如果你真的这么做了,那么在几个小时内建立一个推特/Reddit/脸书或任何克隆都不是真正的方式。你可能会对系统的某些部分使用不同的技术。
当每个上下文中的每个项目都以这种方式构建时,这就成了一个问题。
开发者将重点放在数据库上,它将有哪些列,它们将是什么数据类型,然后他们开始根据数据库中的数据编写代码。
这种心态的问题在于,大多数企业都有自己的流程,并且以他们自己的方式独特存在。
每当执行业务流程时,该流程不仅会将数据保存在数据库中,还会:
它可以根据特定的业务规则和要求验证数据
- 它可能会与第三方服务集成对话
- 它可以将数据存储到多个数据库或数据库表中
- 它可以与多个服务或应用进行通信
这是非常技术性的,这些都是实现细节。业务人员和客户并不关心实现细节,他们关心的只是功能是否有效。
作为项目的新成员,除非你向产品团队或其他工程师提问,否则你可能不得不猜测业务流程中会发生什么。
例如,每当你在送餐服务上下新订单时,想想需要发生什么。它不仅仅是在“订单”表中创建订单
这就是为什么人们发明了一种不同的方法来从商业角度思考业务流程和整个流程中实际发生的事情的行为。
设计送餐服务流程的人员会考虑以下用例:
- 选择要点的餐厅和食物
- 申请优惠券(如果适用)
- 为订单付款批准订单
- 接受订单交付
- 从餐厅取餐
- 将订单送到客户交货目的地
- 完成订单交付
为了成功执行整个用例,这里可能会应用一些额外的步骤或业务规则。
正如你所看到的,这里没有技术术语,只描述了行为。
为了使用这种思维方式,开发人员需要先了解业务领域及其工作原理,然后才能编写代码。
此外,如果您将类、方法和属性的名称与业务人员使用它们的名称完全相同,这将是非常有益的,这样每个人都更容易使用相同的语言并相互理解,而不是业务人员必须学习技术术语。
命名变得容易多了,你不必发明类名和方法名,它们已经存在了。
设计数据库和其他基础设施问题仍然非常重要,但你可以将重点从基础设施问题转移到开发周期的后期。
你可以在不使用任何基础设施的情况下设计与业务相关的代码。
现在的工作重点是解决问题,而不仅仅是编写代码和技术细节。
让我们看一个代码示例,看看在实现一个简单的用例-成员注册时,用 CRUD 思维方式与用业务流程思维方式编写的相同代码的差异:
$member = new Member();
$member->id = $id;
$member->firstName = $firstName;
$member->lastName = $lastName;
$member->emailAddress = $emailAddress;
$member->status = $status;
$member->password = bcrypt($password);
$member->save();
在第一个例子中,重点是在数据库中插入成员,存储哪些字段以及如何存储,哪些是实现细节(本例中使用了 ActiveRecord,其中每个数据库列都链接到对象上的一个属性。DataMapper 类似,但映射和实现不同)。它不会向你显示此处的业务流程。你无法从代码中理解这一点。
$member = Member::signUp(
id: Id::createFromString(value: $this->memberRepository->generateIdentity()),
firstName: FirstName::createFromString(value: $signUpMember->firstName),
lastName: LastName::createFromString(value: $signUpMember->lastName),
emailAddress: $emailAddress,
status: StatusName::PENDING,
createdAt: $this->clock->now(),
updatedAt: $this->clock->now(),
password: Password::createFromString(value: $this->hasher->make(value: $signUpMember->password))
);
在第二个例子中,重点是系统的行为,你可以了解系统发生了什么以及它是如何工作的,它有一个很好的与业务相关的方法名称。如果你关心实现细节以及方法是如何实现的,则可以查看方法本身。
这种类型的代码(示例之一)通常是在应用服务或命令处理程序类中编写的。
这些都是简单的例子,但想想一个更复杂的例子,以及这些代码如何帮助你更好地理解业务:
Order::place()
Reservation::make()
Order::fullfil()
Coupon::apply()
请记住,这种思考和编写代码的方式可能需要一段时间才能适应,而且这在很大程度上取决于你所处的环境。
对于没有特定业务流程的特定情况(如数据管理软件),可以使用 CRUD方法。
在一个非常复杂的软件中,管理代码的复杂性,对代码流(应该执行什么代码以及何时执行)有更多的控制,并以业务流程思维的心态理解业务会容易得多,该软件属于以下示例类别:
Finance
Real Estate
Education
Health
等等,你就明白了。这些业务主要不是在管理数据,而是有每天发生的特定流程。
你能以 CRUD 的思维方式构建这些类别的软件吗?当然,但是使用和管理代码会更加困难。