大家好,今天和大家聊聊三端分离的3-tier模式。
3-tier这个名字看起来很洋气,其实大家可以简单理解成三端分离。对于这个问题可能大家会有些奇怪,究竟是哪三端要分离,又是为什么要分离呢?所以我们先来聊聊三端分离的背景。
如果大家做过后端程序的话,应该对这三端是比较熟悉的。为了防止有些同学不了解,我们来简单介绍一下。
首先是数据端,这里的数据端指的就是数据源。它们返回的数据是原始数据,一般就是刚刚从数据库或者是其他存储引擎里读取出来的结果,数据库存的什么样子就返回什么样。原始数据里既然有“原始”两个字,大家也不难猜测,这个数据想必也比较原始。
比较原始指的是数据的内容没有经过清洗以及处理,也往往不会过滤,所以整个数据可能比较杂乱。大多数情况下我们会对数据进行简单的格式化,比如常用的json格式。大家可以参考一下下图,一般从数据源获取到的数据就是下面这个格式。
业务层的功能也不复杂,就是从数据层获取到所需要的数据之后,对数据层的原始数据进行简单基本的加工,使得它满足业务功能所需要的要求。
举一个简单的例子,在电商网站的秒杀活动当中,如果商品已经售罄,那么前端需要显示一个已经售罄的页面。这个是否售罄的逻辑就是业务层通过逻辑判断传递给前端的,我们从数据库当中获取到的可能只有商品的库存,需要在业务层判断库存是否为0,来判断是否售罄。当然这里只是举一个例子,这个部分要做的数据处理的事情还有很多,凡是根据业务需要进行的数据处理,都可以理解成业务逻辑。
展示端也就是view层,它负责用户直接看到的内容的数据展示部分。
如果说业务逻辑层的功能是处理一些业务上的逻辑,那么展示层负责的是展示逻辑的处理,这里我也举一个很简单的例子,比如说从数据库或者是上游数据当中获得的商品图片往往都是一个索引地址,因为我们是不会直接把商品的图片存放在数据库当中的。但是我们前端展示的时候显然不能展示索引,需要找到对应的图片。
这里的根据索引寻找图片就是一个展示逻辑,这些逻辑一般只和展示相关,但是在一些复杂的场景下,其实也会涉及一些业务逻辑,比如说在推荐系统当中,需要调用模型对商品进行打分,然后根据模型打分的结果进行排序。那么这个调用模型的过程往往也在这一层。这样的逻辑其实还有不少,比如说一些数据的加工,比如我们查找到的结果最后都需要封装成json,这个封装也是一个部分。再比如一些数据可能原始没有,我们需要通过上层的结果来进行一个简单的运算,比如通过click和impression计算CTR等等。
业务层只负责数据的处理,但是展示层需要负责前端的展示,展示需要的数据和逻辑大部分也都在这一层。因此这一层往往比较复杂,也是直接和前端打交道的一层。
看到这里为止,其实3-tier的内容也就基本上讲完了,主要的用途以及特点就是分层。通过这样简单的分层, 我们根据功能把一条完整的链路分成了三个部分。三个部分各司其职,只对自己这一块的部分负责,这样降低了彼此之间的耦合,提升了开发以及维护的效率。
接下来,我们来看个例子体会一下:
# 将数据展示、数据加工和数据处理分开
class Data:
products = {
'milk': {'price': 1.5, 'quantity': 10},
'eggs': {'price': 0.2, 'quantity': 100},
'cheese': {'price': 2.0, 'quantity': 10},
}
statistics = {
'impressions': 120,
'clicks': 10
}
def __getitem__(self, key):
print('Fetching from Data Store')
if key == 'products':
return self.products
else:
return self.statistics
class BusinessLogic:
data = Data()
def product_list(self):
return self.data['products'].keys()
def statistic_information(self, sta):
return self.data['statistics'].get(sta, None)
def product_information(self, product):
return self.data['products'].get(product, None)
class Ui:
""" UI interaction class """
def __init__(self) -> None:
self.business_logic = BusinessLogic()
def get_product_list(self) -> None:
print("PRODUCT LIST:")
for product in self.business_logic.product_list():
print(product)
print("")
def get_product_information(self, product: str) -> None:
product_info = self.business_logic.product_information(product)
if product_info:
print("PRODUCT INFORMATION:")
print(
f"Name: {product.title()}, "
+ f"Price: {product_info.get('price', 0):.2f}, "
+ f"Quantity: {product_info.get('quantity', 0):}"
)
else:
print(f"That product '{product}' does not exist in the records")
def get_CTR(self):
print('CTR is {}'.format(self.business_logic.statistic_information('clicks') / self.business_logic.statistic_information('impressions')))
if __name__ == '__main__':
ui = Ui()
bi = BusinessLogic()
ui.get_product_information('milk')
ui.get_CTR()
在代码当中我们可以清晰地看到它们之间彼此的一个依赖关系,其中业务层依赖数据层的数据,对数据层的原始数据根据业务逻辑进行一定的处理。而展示层又是业务层的下游,对业务层处理完的数据进行进一步地加工,加工成前端所需要的格式,最终用于展现。
这样一层层拆分,可以帮助程序员聚焦自己的工作范围,尤其适合多人协作的场景。但是它也不是完美的,这样分层的结构,其实某种程度上也一定程度上增加了架构的复杂度。因为有些场景下可能我们可以直接访问处理数据,但是现在必须通过中间层中转,其实会导致代码的冗余。但瑕不掩瑜,在目前的后端领域,一般还是采用这样的三层划分的架构。所以了解这个设计模式也是入门后端的基础。