no-stacks.md 9.3 KB

从真实世界到前后端

RePractise终于又迎来了新的一篇,要知道上一篇可是在半年前呢——《Repractise前端篇: 前端演进史 》。照RePractise惯例,这又是一篇超长文以及个人的扯淡过程。

当然这也是一个神奇的标题,因为我已经想不到一个好的名字了,不过先这样吧。这篇文章算是我最近两三个月的一篇思考。在上一个项目的打杂生涯里,我开始去学习架构方面的知识,开始去接触DDD的思想。从编码到架构,再回到实际的编码中,总会有很多的灵感闪现。

从真实世界到前后端

我们所写的代码在某种程度上都反应了真实世界的模型、行为等等。一个比较常见的模型就是:购物模型。同时, 这也是一个很好的展示前后端分离的模型。

store-model.jpg

(PS: 原谅我的画工)

便利店与售货员

对于一般的便利店来说,只有一个销售员,ta负责整个商店的一系列事务。从某种意义上来说,ta就是整个系统的核心,负责了系统的业务和事件。

一般来说在一个购买流程里,会有三个主要的人或物:

  • 售货员。一般来说,ta只会在最后的结账流程中出以及顾客询问时做出响应。
  • 货物。没啥可解释的,就是一堆模型。0
  • 顾客 。浏览商店、对比商店、blabla等等。

如果我们要构建这样一个系统,我们只需要区分出系统的各个部分,那么剩下的事情就变得很简单了。

domain.jpg

由于整个系统仍然是相当复杂的,我们在这里只关注于用户购买的过程。

模型、领域、抽象

从购买过程来说,顾客所要做的事情就是:

  • 浏览、对比商品
  • 加到购物车
  • 结账、付钱

对应的也就是有模型、领域和抽象几个概念。

模型

这些商品实现上就是相当于一系列的模型及数据。在用户购买之前,我们只需要一个去获取一个个的数据接口,并展示这些数据。

对应于这些商品要建起Schema来是一件容易的事。作为一个商品,他们都拥有着一些共同的元素:price, name, description, location, manufacturer等等的信息。其中一些属性,还会有复杂的对应关系:

Product Schema

这些需要在我们建立数据库的时候,尽可能地明确好这些关系。由于业务本身是难以预料的,你可能和我们之前的项目一样需要一个addtionInfo的字段,来用JSON存储一些额外的字段。当然如果你使用的是NoSQL,那就再好不过了。

最好你还使用了读写分离架构,一种比较常见的用法就是CMS网站,人们使用数据库来存储内容,使用静态页面来展示这些内容。比较好的实践还有CQRS(Command Query Responsibility Segregation, 命令查询职责分离模式),用于CRUD(增、删、改,当然也可以查)的Command,以及Query的查询分开。简单的来说,就是有两个不同的数据持久化中心:

Basic CQRS

这一点特别适合于那些查询、搜索为主的网站,如淘宝。哈哈,我们离题有点远了,总之我们就是在这里提供了数据库的灵气,并对一些字段做一些简单的处理。听上去感觉GraphQL更适合做这样的事。

领域

而顾客及售货员在整个过程中做的事情就是领域(Domain,《实现领域驱动设计》)——即一个组织所做的事情以及其中所包含的一切。对于顾客和售货员来说,他们在各自的领域里做着不同的事。

对于顾客来说,其整个浏览过程,基本上都是在前端完成:

  • 搜索、查找商品 -> 获得商品列表
  • 查找商品详细
  • 切换到下一个商品

这个场景下就特别适合于上面说到的读写分离架构。在浏览过程中,对用户的数据进行监控,以用于了解用户的行为,改善用户体验。这也是很常见的功能,或者说他们是无处不在的模式:

  • 结果页 / 列表页
  • 详情页

随后的用户收藏、添加到购物车、购买、交付等流程都需要与后台进行更紧密的交付。而这些都和售货员都有紧密的关系,而这些就不是一种简单的事。

从用户购买完成以后,剩下的就是一堆琐碎的事了,而这些都是由后端来完成的:

  • 订单子系统
  • 物流系统
  • 发票系统
  • 支付系统

等等。

对于用户来说,一种最简单的情况就是亚马逊,你只需要按一下“一键下单”即可。不需要关心后面的操作了,同样的这也适合于我们的业务场景。

抽象

抽象本来不打算写的,但是后来想了想还是写。总的来说整个过程还是需要相对比较好的抽象能力,要不我就很难讲述清楚这里面的过程了。

抽象是很神奇的东西,也可以分为几个不同的境界——但是我也不知道有几个境界,简单的来说就是不同的人看上去就有不同的东西。如有的人看到下面的画就是一坨shit——还不如小学生画的呢,有的人就会惊呼大师。

星空

反正,我也很看不懂。这一点倒类似于最初我对设计模型的理解一样:

  • 一开始不以为然
  • 然后发现很棒
  • 接着使用过度
  • 最后就和最好的编程器Emacs一样

Editor Learning Curve

这些都在随着编程生涯的展开而发生一些变化,我们不断地抽象出一些概念,以至于到了最后刚进入这个行业的人都看不懂。但是,这些都是一点点在一层层抽象的基础上产生的。

Needs

所以,我就把这一小小节扯完了,反正我是不想说得太抽象了。接着,让我们再扯点技术性的话题。

前后台分离:后台

典型的Web应用框架就是类似于这样的架构:

Spring Web App Architecture

又或者是MVC架构,但是这已经不重要了。我们都前后端分离了,是时候把V层去掉了。

MVC Role Diagram

我们继续以上面的浏览来购买流程来做扯淡,后台除了提高上面的商品信息以外,在购买的时候还需要对用户进行授权。当然注册就是另外一个话题了,另外一个很大的话题。

所有的这些我们都可以向前台提供对应的API即可。理想的情况下,我们对应于不同的模块可以有不同的服务:

MicroServices

但是现实并不总是这么美好的,而在我们当前情况下则可以——毕竟所有的用户都应该能浏览所有的商品,这时就不需要做特殊的处理了。

在这个过程中,我们还有一系列的操作需要在后台来完成。不过,这些都可以在内部中完成的。而复杂的过程,实际上还存在于前端的逻辑当中。

前后台分离:前端

开始时,我们需要这样做去获取一个个的商品详情。这些数据也可以在返回页面模板的时候,直接将API数据填充到HTML中——带后台渲染的React都是这样做的。然后在用户浏览的过程中,我们还需要遵循这样的数据流程:

  • 获取数据。无论是Ajax,还是新的Fetch API都可以做这样的事。
  • 处理数据。依据于业务的需要对数据进行一些特殊的处理,如修改时间、价格的显示格式,描述的长度等等。
  • 显示数据。只需要一个简单的模板引擎,过去的JSP、Mustache、今天的React都在做同样的事。

而在进入这个页面之前,我们还需要关注几个基本的元素,虽然这些都是将由框架来解决的。

  • 路由。无论你遵不遵循REST的实践,我们只需要给对应的页面一个URL即可。相应的如果我们需要SEO,那么我们也需要在后台有一个对应的URL。理想的情况下,他们应该是由一个URL。
  • 模块化。从过去Require.js的火热,到今天的各式各样的框架内建的模块化框架,他们解决都是一个问题:代码度的问题。这一点和后台采用的微服务架构的缘由好像是一样。
  • 控制器。我也想不起来为什么控制器非在这里不可,但是我想只有这样,我才能快速地找到这个文件。

作为前端,我们不仅仅要负责的页面的美观,还要对用户的事件做出响应,并且做好用户体验。

  • 事件

这就是最近火热的关于DOM的讨论的原因,当顾客想了解一下一些不一样东西的时候,我们就需要对DOM进行操作。

我突然就有一个问题了,你真的有那么多DOM需要操作么?你又不是Facebook,有那么多的Timeline和事件流。还有你的用户体验是不是做得足够好了?顺便再补一刀,如果你使用了React,并且没有进行前后端分离,那就等下一个恶梦。

最后,当用户买下东西的时候,我们也需要这样的交互流程。

RePractise

因为最近我对DDD又有了一些想法,还在想着如何直接由真实世界来建模。顺便整理了这些思路到一起,但是好似这样的设计更简单。