不久前,在StackExchange网站上,一位名为 SLC 的用户提起他正在设计一个ASP.NET网站,他对于是否要将后端设计为API有些举旗不定,希望能够得到一些建议。一石激起千层浪,这个帖子很快得到了大量的关注与回复。现在让我们来了解一下SLC所面临的具体情况与问题。
SLC设计的网站是一个典型的ASP.NET MVC应用,他在开发时使用的都是一些经典的MVC模式,即控制器、视图与模型的结合,在控制器的方法中调用某个Manager对象以获取数据并创建视图模型。本来没什么问题,但他的某位同事对此表示异议,认为这种代码的耦合性太强,如若要创建一个桌面版本的应用,这些代码就无法重用了。他所推崇的最佳实践是创建一套API,在此基础上开发网站、桌面应用或者移动应用就都不成问题了。
SLC对此看法持保留意见,他列举了几条他认为这种做法不合适的原因:
支持者
用户 gbjbaanb 坚决支持使用API设计后端的方式,他认为这种方法不仅使得代码可以重用,并且能够带来更高的安全性与更好的设计。而一体性的实现会造成架构的僵化,难以扩展、替换及改进。他还以时下最流行的 微服务 为例,在这种设计中的后端实现为多个小型的服务,它们各自提供一套API以供客户端进行调用。他同时也指出了SLC对于API的概念存在误解之处,API并非一种远程类库,而更像是一种提供数据的服务。网站可以通过API获取数据,并在本地对数据进行某些操作。
gbjbaanb也对SLC所列举的部分观点进行了点评:
gbjbaanb最后为SLC提了一些设计方面的建议,从分层架构的角度看,网站是一种展示层,而API则是应用层。对于业务逻辑的设计可以看情况选择以数据为中心,或是以领域为中心的方式,当然后者显得更“纯净”一些。
反对者
而反对API的声音也不少。 fotijr 表示虽然微服务是当下的热点,但这不代表这种做法总是值得的。松耦合固然是好事,但如果因此让整个开发周期变得过于痛苦,那就得不偿失了。他同时建议将模型放在一个独立的数据访问项目中,以便MVC或桌面应用可以直接重用。 JacquesB 也不支持设计为API的做法,他认为单纯地创建API并不能够保证松耦合性,如果设计不当,反而会出现跨服务器边界的紧耦合性。
一点拙见
接下来笔者将简单地表达一下对此问题的个人意见。首先是对API的理解,原作者似乎将API简单地理解为一种独立进程的分布式的API,例如Web API、Remoting或RPC调用。其实从广义上来说,只要是通过代码调用后台的业务逻辑与数据,都可以理解为一种API。因此问题不在于要不要设计API,而是如何设计这套API。
接下来看一下原作者的设计方式,他所使用的方法是在控制器方法内调用某个Manager对象的方法以访问数据,那么大概可以推测出他的业务逻辑是以一种以数据为中心的设计思想,所使用的模式很可能是 事务脚本 (Transaction Script)。这种方式的优点在于简单易懂,但这样设计出的模型很可能是一种 贫血模型 ,即业务逻辑大量散落在对应于用例的应用层的方法中,随着时间的推移,其难以维护的缺点将会逐渐暴露出来。
比较好的方式是遵循关注分离的设计原则,将业务逻辑与对应用例的应用逻辑进行隔离,将业务逻辑放在一个具有高度内聚性的领域层中,而展示层(即网站、桌面或移动应用)通过应用层间接地调用相应的业务逻辑。从这一角度来说,SLC所需的API正是应用层所暴露的方法与对象(例如 数据传输对象 ),而他本人在之后的回复中也表示了对这一点的理解。至于是否要将应用层的API设计为分布式,这已经不是主要的问题所在了。
关于通过API实现重用性,让同一套逻辑能够适用于不同的展示层,这一点笔者并不认同。诚然,在分层架构(或其它类似的架构)中,理论上是有可能重用相同的应用逻辑的。但在实际应用中,由于客户端的不同特性,往往会对应用逻辑进行某种程度的调整,以实现最优化的用户体验。在网站中使用的Composite UI风格,在移动端可能会被设计为Task Based UI,而UX以及用例的区别将直接导致应用层API的变化。如果要坚持重用应用逻辑,势必要对UX作出某种程度的妥协,在做出重用这一决定时,软件组织必须对它的影响有深刻的理解。
其实在笔者看来,分层的最大好处在于保持了设计与代码的整洁性,有利于长期的设计演变与代码维护,并且能够促进单元测试,同时可以对不同的层进行并行开发、测试与部署。相信这也是SLC的同事的本意所在。
感谢丁晓昀对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入InfoQ读者交流群 )。