今天,我对我们的MVC应用程序进行了激烈的讨论。我们有一个用MVC(ASP.NET)编写的网站,它通常遵循在视图中执行某些操作的模式->击中控制器->控制器构建模型(称为获得数据的Manager,然后在控制器方法本身)->模型可以查看->冲洗并重复。

他说我们的代码太紧密了。例如,如果我们还需要桌面应用程序,则将无法使用现有代码。

他说的解决方案和最佳实践是建立一个API,然后在API的顶部,然后构建桌面应用程序,移动应用程序等非常简单。出于各种原因,这对我来说似乎是个坏主意。

我似乎无法通过谷歌搜索找到任何可以讨论这种做法的东西。任何人都没有关于优缺点,为什么要这么做,为什么不应该读书或进一步阅读的信息吗?

出于某些原因,我认为这是个坏主意:


太抽象了,无法通过API运行后端。您正在尝试使其过于灵活,这将使其变得难以处理。
MVC中内置的所有内容似乎都无用,例如角色和身份验证。例如,[授权]属性和安全性;您必须自己动手。
您所有的API调用都需要附带安全信息,并且您将必须开发令牌系统等等。
您将必须为每个函数编写完整的API调用。您的程序可以做到。您几乎要实现的每种方法都需要使用API​​。每个用户都有一个Get / Update / Delete(获取/更新/删除),以及每个其他操作的变体(例如更新用户名,将用户添加到组等),并且每个操作都是不同的API调用。关于API的工具,例如接口和抽象类。像WCF这样的东西对接口的支持非常薄弱。
您有一个创建用户或执行某些任务的方法。如果要创建50个用户,则只需调用50次即可。当您决定将此方法用作API时,本地Web服务器可以命名管道连接到它,也没有问题-您的桌面客户端也可以访问它,但是突然间,您的批量用户创建将涉及在Internet上锤击API 50次,不好因此,您必须创建一个批量方法,但实际上您只是在为桌面客户端创建它。这样,您最终不得不a)根据与其集成的API修改API,而不能直接与其集成,b)做更多的工作来创建额外的功能。
YAGNI。除非您专门计划编写两个功能相同的应用程序,例如一个Web和一个Windows应用程序,否则这将是大量的额外开发工作。 -end。
很多独立的操作将需要大量的来回操作,例如一些代码可能会获得当前用户,检查该用户是否具有管理员角色,获得该用户所属的公司,获得一个列表其他成员,请向他们发送所有电子邮件。这将需要大量的API调用,或为您想要的特定任务编写定制方法,而该定制方法的唯一好处是速度快,但缺点是灵活性不足。

在我看来,除非您真的需要两个相同的应用程序,否则确实不值得。我也从未见过这样构建的ASP.NET应用程序,您必须编写两个单独的应用程序(API和您的代码),并同时对它们进行版本控制(如果您的用户页面有一个新字段,则d必须同时更新API和您使用的代码,以确保没有不良影响,或进行大量额外工作以保持其健壮性。)


编辑:一些不错的回应,现在真的开始对这一切意味着什么有了一个好主意。因此,为了扩展我的问题,您将如何构建一个MVC应用程序以遵循此API结构?例如,您有一个显示用户信息的网站。在MVC下,您具有:

视图-(CS)HTML页面,其中显示UserViewModel
控制器-调用GetUser()并创建一个UserViewModel并将其传递给视图
Manager具有GetUser方法的类(与您的API相似)。

控制器执行GetUser(),但您也需要桌面应用程序。这意味着您的GetUser需要通过某种API公开。您可能需要TCP连接,或者是WCF,或者可能是Remoting。您还需要一个移动应用程序,该应用程序将具有REST风格,因为持久性连接不稳定。

然后您是否将为每个应用程序编写一个API,具有方法GetUser()的WCF Web服务和代码return new UserManager().GetUser()吗?和做同样事情的MVC 4 Web API方法呢?继续在您的MVC控制器方法中直接调用GetUser吗?

还是您选择适用于所有这三种解决方案(Web api REST服务)的解决方案,并在此基础上构建一切,所以所有这三种应用都可以使用API调用(将mvc调用到本地计算机)。

这只是理论上的完美方案吗?我可以看到以这种方式进行开发的开销很大,尤其是如果您必须以一种允许您以RESTful方式进行操作的方式进行开发时。我认为其中一些已包含在答复中。


编辑2:在阅读了更多内容之后,我在下面提出了一条评论,我认为这可能会解释它。我认为这个问题有点棘手。如果您将后端编写为API,让我感到困惑,我认为应该有一个单一的Web服务,所有东西(MVC应用程序,桌面应用程序,移动应用程序)都可以调用来完成工作。

我得出的结论是,您真正应该做的是确保业务逻辑层正确分离。查看我的代码,我已经这样做了-控制器将在管理器上调用GetUser(),然后从中创建视图模型以使用View进行渲染。因此,实际上,业务逻辑层是一个API。但是,如果要从桌面应用程序调用它,则需要编写WCF服务之类的内容以方便调用它。即使仅使用包含代码GetUser()的WCF方法return MyBusinessLayer.GetUser()也已足够。因此,API是业务逻辑,而WCF / Web api等只是让外部应用程序调用它的一小部分代码。根据您的需要,使用不同的API来创建业务逻辑层,您将必须为您希望其他应用程序执行的每个操作编写一个API方法,此外,您还需要找出一种进行身份验证的方法,但是在大多数情况下一样的。将您的业务逻辑放在一个单独的项目(类库)中,您可能不会有任何问题!

希望这种解释是正确的。感谢您进行的所有讨论/评论。

评论

您能否揭露您认为这是个坏主意的原因?如今,我必须承认,我认为没有理由不这样做。除其他优点外,它还使您可以轻松地将应用程序移植到不同的平台,并在前端具有极大的灵活性,而无需触摸后端代码...

@SLC:您说的API是指像SOAP或REST接口这样的Web服务API?因为您应该使后端成为一个API,但不应该使它成为Web服务。

@IanNewson“例如,一个移动应用程序往往功能较少。”我从未听说过令人信服的理由,为什么移动应用程序应成为二等公民……(但每个人似乎都是这样做的)

@IanNewson也许只是我...但是我总是发现自己因无法在移动设备上做某事或其他事情而受挫,以至于我在移动设备上很少做事

您说YAGNI适用,但是我的经验是应用程序或者每两年重新编写一次UI,或者每个人都抱怨他们确实需要一个。如果我们不会因为新的前端技术到来而丧失业务逻辑,那肯定会很好。

#1 楼

是的,您应该这样做。

它不仅可以使后端重新使用,而且可以提供更高的安全性和更好的设计。如果您将后端作为单个系统的一部分进行编写,那么您正在进行的单片设计将很难扩展,替换或增强。后端被分成许多小(甚至大)服务,每个服务都提供客户端系统使用的API。如果您想象在应用程序中使用许多第三方数据源,那么您意识到您可能已经在这样做了。

另一个好处是,可以将每个服务的构建和维护交由不同的团队进行。 ,他们可以向其中添加不影响任何其他生产产品团队的功能。只有完成并发布服务后,您才开始向产品添加功能以使用它们。这样可以使开发过程更加顺畅(尽管总体上可能会变慢,但是您往往会获得更好的质量和易于理解的内容)。您将API视为远程库。不是。将服务视为更多的数据提供服务。您调用该服务以获取数据,然后在本地对该数据执行操作。要确定用户是否已登录,您可以调用“ GetUser”,然后查看'logged on'的值。 (当然,YMMV带有该示例)。

您创建批量用户的示例只是在找借口-这里没有区别,无论您在单片系统中可以完成的操作还是可以在服务体系结构(例如,您将传递一组用户以进行批量创建,或者传递一个用户来创建。您仍然可以对服务进行完全相同的操作。)

MVC已经基于隔离服务的概念,只有MVC框架将它们捆绑到单个项目中。这并不意味着您会失去任何东西,除了框架为您提供的捆绑帮助程序之外。使用不同的框架,您将不得不使用不同的帮助器。或者,在这种情况下,可以自己滚动(或使用库直接添加它们)。

调试也很容易-您可以单独对API进行彻底测试,因此无需对其进行调试(并且您可以端到端调试,Visual Studio可以同时连接到多个进程。)

实现安全性的额外工作之类的事情是一件好事。当前,如果您将所有代码捆绑到您的网站中,如果黑客获得了访问权限,那么他们也将访问所有内容,包括数据库。如果将其拆分为一个API,除非他们也对API层进行了黑客攻击,否则黑客将无法对您的代码做太多工作-这对他们来说将是非常困难的(曾经想知道攻击者如何获得所有网站用户或抄送详细信息的庞大列表?这是因为他们入侵了OS或Web服务器,并且它直接连接到数据库,可以轻松地运行“ select * from users”。 -服务器应用程序)这样编写。当我在金融服务行业工作时,没有人会写一个网站,一方面是因为它存在太多的安全风险,另一方面是因为很多开发工作都是基于GUI(而不是稳定的后端数据处理)的系统。使用服务风格的体系结构将DP系统作为网站公开很容易。

第二次编辑:主题上的一些链接(用于OP):请注意,在网站上下文中谈论这些内容时,应该将Web服务器视为表示层,因为它是客户端调用其他层,并且还因为它构造了发送到浏览器进行渲染的UI视图。这是一个很大的主题,有许多方法可以设计您的应用程序-以数据为中心或以域为中心(我通常认为以域为中心是“纯净的”,但YMMV),但这全都归结为两者之间的逻辑层您的客户和您的数据库。如果您认为中间层,API层等同于您的模型,则有点像MVC,只是模型不是数据库的简单包装,它更丰富并且可以做更多的事情(例如,聚合来自2个数据源的数据, -处理数据以适合API,缓存数据等):


多层体系结构
在.NET中构建N层应用程序
Youtube教程-也请参阅相关链接,其中一个要在C#中实施
Pluralsight培训课程(免费试用或付费)


评论


从建筑宇航员的角度来看这是肯定的吗?我可以从服务的角度理解您的第二段和第三段,但是我们正在谈论的是GetUser,CreateUser,IsUserLoggedIn和数百个微小的函数,这些函数以前是单行代码,可以转换为API调用。

– NibblyPig
15年6月25日在13:19

想象一下,您正在将其编写为网站-所有这些微小的功能都无法像您想象的那样具有交互性,因此您在构造页面时必须获取数据并将其缓存在本地(或将它们作为可能过时的数据传递给网站)。客户,视系统而定)。在很多情况下,您必须将设计从“按需响应”更改为“预先预期”,但是大多数系统将进行API调用。将您的API设计为粒度较小,以数据为中心,因此IsUserLoggedOn不必是API调用,只需要一个“ GetUserDetails”,然后在本地检查即可。

– gbjbaanb
15年6月25日在13:25

我们在我最后的工作地点使用了这种方法,效果很好。我们的主要产品是一个Web应用程序,但是我们能够创建一个桌面应用程序甚至Excel工作表,这些应用程序可以访问与我们的Web应用程序相同的Web服务来处理所有数据,并向客户公开这些服务,以便他们对他们编程。

– Kik
2015年6月25日在21:06



这还有另一个好处:您可以向网站客户公开后端API。在我们公司,我们做到了这一点,一些大型软件公司的客户(在我们的主机上试用了后端)付了钱,将后端全部包装为自托管产品。根据产品的不同,一些客户对前端贴面的兴趣较小,而对您的产品实际功能(后端)的兴趣更大。那是另一种要出售的产品。

–里德
15年6月25日在22:28

这也使使用Web服务中的相同逻辑变得更加容易。我们的团队一直认为我们永远不必做的事情之一...这也使单元测试变得更加容易。

–ps2goat
15年6月26日在21:13

#2 楼

您可能无法避免构建API。即使您构建“仅一个网站”,它仍将需要以某种方式从后端获取其数据。但是,您决定这样做,那就是事实上的API。

知道这一点,真正的问题不是是否要构建API,而是如何构建它。您可以作为临时对象即时进行操作-实际上,许多网站都是以这种方式构建的-或者您可以精心设计使其可以在其他情况下使用。在这种情况下,很明显您的同事是对的:您应该首先做API,然后在它之上构建您的网站。

但是,这带来了一些问题,因为你指出。要解决这些问题:


太抽象了,无法通过API运行后端。您正在尝试使其过于灵活,这将使其变得难以管理。


这取决于您的操作方式。正如乔治·波利亚(GeorgePólya)在其出色的著作《如何解决》中指出的那样,通常“更普遍的问题可能更容易解决”。这称为发明人悖论。在编程的情况下,它通常是通过关注点分离的方式工作的:您的后端不再需要关心它放入和取出的数据的格式,因此其代码可以简单得多。您的数据解析器和渲染器不再需要担心它们创建的数据会发生什么,因此它们也可以变得更简单。通过将代码分解为更易于管理的块,所有这些工作都可以进行。


MVC中内置的所有内容似乎都无用,例如角色和身份验证。例如,[授权]属性和安全性;您将不得不自己动手。


我承认,我很难同情那些拒绝学习工具的人。仅仅因为您不了解它们的使用并不意味着它们是无用的,并且当然并不意味着您应该自己动手。恰恰相反;在理解替代方案之前,您不应该自己动手开发工具,以便可以确保解决与替代方案相同的问题(即使仅以您自己的方式)。

考虑Linus Torvalds ,他以编写Linux最著名,但也写了git:现在是世界上最受欢迎的版本控制系统之一。他设计中的驱动因素之一是对Subversion的强烈反对(另一个非常流行的VCS,可以说是git编写时最受欢迎的一种)。他决心采取Subversion可以采取的一切措施,并在可能的范围内以不同的方式解决这些问题。为此,他必须自己成为Subversion的专家,以使他能够理解相同的问题领域并采用不同的方法。

,或者,在学习工具的过程中,您可能会发现它们按原样很有用,不需要替换。


您所有的API调用都需要附加安全信息,并且开发令牌系统之类的东西。


是的。这应该是这样。


您将必须为程序将要执行的每个函数编写完整的API调用。您几乎要实现的每种方法都需要使用API​​。每个用户都有一个Get / Update / Delete(获取/更新/删除),以及每个其他操作的变体,例如更新用户名,将用户添加到组等,等等,每个操作都是不同的API调用。
不必要。这就是像REST这样的体系结构发挥作用的地方。您确定应用程序使用的资源以及适用于这些资源的操作,然后实现这些资源而不必担心其他资源。


您会丢失各种涉及接口的工具和抽象类。像WCF这样的东西对接口的支持非常薄弱。相反,使用API​​时,接口变得越来越重要,而不是更少。它们出现在您渲染它们的表示形式中。如今,大多数人为此指定基于JSON的格式,但是只要指定得当,您就可以使用所需的任何格式。您可以在后端将此格式的调用输出呈现出来,并将其解析为前端上您想要的任何内容(可能是相同类型的对象)。开销很小,灵活性方面的收益也很大。


您有一种创建用户或执行某些任务的方法。如果要创建50个用户,则只需调用50次即可。当您决定将此方法用作API时,本地Web服务器可以命名管道连接到它,也没有问题-您的桌面客户端也可以访问它,但是突然间,您的批量用户创建将涉及在Internet上锤击API 50次,不好因此,您必须创建一个批量方法,但实际上您只是在为桌面客户端创建它。这样,您最终不得不a)根据与其集成的API修改API,而不能直接与其进行集成,b)做更多的工作来创建额外的功能。 />
创建现有方法的批量版本几乎不是我所说的“更多工作”。如果您不担心原子性之类的问题,则批量方法可能只不过是原始方法的非常薄的前端而已。


亚尼除非您专门计划编写两个功能相同的应用程序,例如一个Web和一个Windows应用程序,否则这将是大量的额外开发工作。


不,YANI(您已经需要它)。我如上所述。唯一的问题是要投入多少设计。
为什么您不能一步一步走遍端头?

但更重要的是,能够以易于识别的格式来回检查数据消除所有显示残骸实际上往往使调试更容易,而不是更困难。


很多独立的操作将需要大量的来回操作,例如某些代码可能会获得最新的用户,检查该用户是否具有管理员角色,获取该用户所属的公司,获取其他成员的列表,并向他们发送所有电子邮件。这将需要大量的API调用,或者为您想要的特定任务编写定制方法,而该定制方法的唯一好处是提高速度,但缺点是灵活性不足。 > REST通过处理完整的对象(资源,使用REST理论的术语)而不是对象的单个属性来解决此问题。要更新用户名,请获取用户对象,更改其名称,然后将用户放回原位。您也可以在更改用户名的同时进行其他更改。更普遍的问题变得更容易解决,因为您可以消除所有用于更新对象单个属性的单个调用:只需加载并保存它。

在某些方面,这与硬件方面的RISC体系结构没有什么不同。 RISC和CISC(其前身)之间的主要区别之一是,CISC体系结构倾向于包括许多直接在内存上运行的指令,而RISC体系结构往往主要在寄存器中运行:在纯RISC体系结构中,唯一的内存操作是LOAD(将某些内容从内存复制到寄存器中)和STORE(从寄存器中获取一个值并将其放入内存中)。内存,这会降低计算机的速度。但是实际上,情况恰恰相反:处理器(客户端)在两次访问内存(服务器)之间进行了更多工作,而这正是加速的来源。

长话短说:您的同事是对的。这是要走的路。作为一项前期工作的交换,它将大大简化您网站的代码并实现与其他网站和应用程序的更好集成。那是值得付出的代价。

进一步阅读:


REST API设计-资源建模


评论


即使这些具有事实上的API。他们倾向于使其他许多开发人员感到恐惧,但是它们都是相同的API。只是设计不是很好。

–最傻的
15年6月26日在12:59

这导致了一个非常差劲的API:太差劲了,以至于很多人甚至都不认为它是API。但是它仍然定义了前端与后端交互的方式,尽管这种方式可能很粗糙。将其视为API有助于了解做好它的重要性。

–最傻的
15年6月26日在17:32

我认为Linus之所以成为git是因为Linux社区反对使用用于内核的商业Bitkeeper DVCS。

– gbjbaanb
15年6月26日在22:03

您的第一句话消除了我的所有困惑。我将术语API与Web服务相关联,这就是我如此困惑的主要原因。

– NibblyPig
15年6月27日,0:05

@IanNewson:有一种方法可以与代码交互,称为http。它可能有很多不相关的要求,并返回了许多不相关的数据,但这就是使它成为糟糕的API的原因。

– jmoreno
15年6月29日在6:08

#3 楼

我知道微服务现在风行一时,但并不总是值得的。是的,目标是松散耦合代码。但这不应以更痛苦的开发周期为代价。

一个好的中间立场是在解决方案中创建一个单独的数据项目。数据项目将是.NET类库。然后,您的ASP.NET MVC项目将添加对数据库的引用,并且所有模型将从数据项目中提取。然后,当需要创建桌面或移动应用程序时,您可以引用相同的代码。因此它可能不是官方的API,但可以作为一个正式的API。如果要使其可以通过API进行访问,则可以创建一个简单的Web项目,充当数据项目的包装器。

评论


我必须维护一个将逻辑放在UI层中的项目,调用相同的共享数据结构。因此,我不得不修复一个bug三十次(“如果再次需要使用相同的逻辑,我们将复制并粘贴!不需要API”)。如果有一个逻辑层(现在有),只需要一个修复就足够了。

– SJuan76
15年6月27日在17:55

除了将该库包装到其自己的NuGet包中并托管自己的NuGet包feed / server之外,此答案也是一个不错的方法。您无需担心冗长的网络,并且可以将所有调用都本地化(并因此更快)到线程中,再加上通过NuGet向类库中引入正确的版本,可以使其他团队在升级时具有灵活性。

– Greg Burghardt
2015年6月30日13:02

#4 楼

不,你不应该。如果您没有立即计划创建访问同一后端的替代前端(如移动或桌面应用或单独的Web应用),那么您不应该引入Web服务层。 YAGNI。

松耦合始终是可取的(同时具有很高的内聚性),但这是一种设计原则,并不意味着您必须物理上将不同服务器上的对象分开!设计不好的服务API可能会跨服务器边界建立紧密的耦合,因此拥有API不能保证松散的耦合。

如果将来需要使用服务API,您可以随时在那一点。只要您将代码保持良好的分层(数据访问和业务逻辑与UI逻辑完全分开),现在就不难介绍。当设计满足实际需求时,最终的设计会更好。这个问题只说了API,但是API也只能表示一个库的接口,然后当然每个层都将定义一个API。最重要的是,您的业务逻辑和数据访问层应在设计级别与UI逻辑完全分开,但是如果不需要,则不应引入Web服务层。

评论


设计不好的东西都是不好的。建立一个API不会花费更多的时间,并且可以适应未来的发展。如今,适应变化的能力至关重要,可以更好地建立强大的基础来满足您甚至不知道的任何需求,但是可能比您想象的要早...

– Laurent S.
15年6月25日在21:37

@Bartdude:为“面向未来”的未来引入不必要的复杂性,这将是浪费资源。

–雅克B
15年6月26日在6:52

@Bartdude添加一个api肯定是更多的时间。不知道您如何认为可以提出其他要求。

–伊恩·纽森(Ian Newson)
15年6月26日在7:19

“您不应该引入Web服务层” API!= Web服务。如果您的API具有业务逻辑,则可以在某个时候将该API作为Web服务公开。不过,这不是一个先决条件。

–Celos
15年6月26日在7:35

@JacquesB:...因此,如果不确定不确定是否需要功能,则实际上不会开发功能。我从YAGNI那里了解到这一点。然而,架构不是功能,错误的架构选择可能(很可能会)导致惨痛的失败。我再一次假设这种讨论甚至可能发生,有时不是出于预算,上市时间,资源或缺乏知识的原因而发生的情况……我想我们完全可以同意不同意我经常与自己进行同样的讨论,所以我理解您的观点^ _ ^

– Laurent S.
15年6月26日在8:56



#5 楼

我公司有一个这样构建的应用程序。最初,我们被委托为另一个开发人员正在创建的前端使用API​​构建后端。当其他开发人员无法开发该前端时,我们也被委托构建前端。尽管此方法肯定有好处,但有一个巨大的缺点:成本。由于要维护的代码更多,并且还要部署两个单独的系统,因此初始构建的成本将大大提高,而持续的维护成本将更高。由于额外的成本,这应该永远是业务决策,而不是开发人员的一时兴起。由于这种方法。您没有描述正在为哪种公司工作的项目类型,但是如果您是一家初创公司产品的制造商,那么交付额外的成本可能就是交付一些使您的产品成为可能的功能之间的区别成功。

另一个(至少不是普遍地)不这样做的原因是,如果或者当您决定创建第二个接口时,很少有一对一的功能映射。例如,如果您制作一个移动应用程序,它们往往功能较少。这意味着您的某些API方法将永远不会被重用。因此,与同事的折衷办法可能是在您之间决定最关键/最关键的调用,并将这些调用添加到API中,并对其他所有事情使用更传统的方法。

要考虑的另一点是您的同事就是说您将无法重用现有的代码,如果您的业务逻辑有些分离,那是不正确的。您只需要围绕内部API创建一个瘦Web服务包装器,这并不是一个特别大的任务。认为您可以将Web服务层重用于另一个前端而根本不做任何更改,这是天真的想法。

#6 楼

这取决于应用程序的类型和您所在的市场的类型。

采用这种方法会有很多取舍和好处。一个方法比另一方法更好不是一个明确的答案。

我将从个人经验谈起。我是2007年决定采用此方向工作的代码库的人。该代码库现在大约有一百万行代码,其中一半是隐藏在大量Web服务背后的服务器代码。 API的另一半是客户群,桌面原生,桌面Web,移动,后端集成等……这个决定并非没有缺点,但基于20/20的见解,我可以说我会再次。让我指出其中涉及的一些权衡。

好处


灵活性。无论是构建移动应用程序以增强桌面体验的请求,还是与SAP后端集成的请求,当您已经具有要调用的API时,一切都会变得更加容易。当您获得足够多的这些请求时,您将有机地朝着API演进,唯一的问题是,它前面是否有标准的Web服务,还是它是为Web服务量身定制的内部API。 br />(团队的)可伸缩性。在我们的案例中,我们有许多不同的开发人员组,所有开发人员都在此API的基础上构建。我们甚至有专门的API团队,他们与不同的小组进行交谈,总结需求,并从中构建通用的API。到了现在,我们甚至都没有被告知人们是在API之上构建东西,而不是每个人都对我们公司有用。
安全性。在代码库的不安全部分和安全部分之间进行清晰的划分有助于推断安全性。将UI和后端代码混在一起往往会使事情变得混乱。灵活性。您必须完成将“正确”构建到API中的工作。从UI代码内部快速运行数据库查询以解决特定问题是不可能的。另外,实际上可重用的API必须考虑很多用例,以至于快速解决方案通常是错误的解决方案。该API的发展灵活性越来越差,特别是因为已经有太多客户端代码(因此,我们正在过渡到版本化的API)。
初始开发速度。毫无疑问,首先开发API的速度较慢。只有在API之上构建了足够多的客户端时,您才能赢回它。但是随后您发现,在API变得足够通用之前,您需要3种不同的客户端实现。我们发现大多数最初的API设计都是错误的,因此不得不大力修改关于如何构建Web服务的准则。

红鲱鱼

。实际上,它们实际上并不重要。


抽象。您的API变得足够抽象,可以涵盖产品需要服务的所有用例,仅此而已。即使没有Web服务,您也将具有执行此操作的内部API,或者具有大量重复的代码。与复制相比,我更喜欢抽象。
放弃服务器端MVC堆栈。如今,一段时间后,几乎所有系统都需要一个移动应用程序。然后,当您构建Web服务以迎合该移动应用程序时,无论如何,您将必须弄清楚如何在API上下文中进行身份验证和授权。当您只有一种方法(在Web服务中执行该操作的方法)时,实际上工作量会减少。
批量操作。通常通过创建一个批量API来解决,该API启动一个后端作业并返回一个作业ID以进行状态查询。没什么大不了的。
调试。我发现总的来说,对系统进行故障排除变得稍微容易一些。您仍然可以在前端和后端代码中都设置断点,因此在实践中并没有那么困难,并且您可以构建自动化的api测试并检测用于监视生产系统的api。 >很多独立的运作。这是您如何设计事物的问题。如果您坚持使用纯CRUD API,那么可以,您将遭受此问题的困扰。但是,通常可以使用一些CQRS API进行扩充,如果您确定自己拥有一个内部API,并且该服务是前端,那么您可以轻松地重用该内部API来为那些特定的对象构造服务

在摘要中
在足够不同的上下文中使用的系统中,API会自然地发展,因为它是满足所有需求的最简单方法。但是肯定有YAGNI发生的情况。需要权衡取舍,直到有道理才有意义。关键是不要教条,对开放的体系结构保持开放的态度,以满足产品不断发展的需求。

评论


有趣的读物,您能否详细说明在设计API时做错了什么以及学到了什么?

–aaaaaaaaaaaa
15年6月27日在18:03

三个主要错误是:(1)使api过于适合主ui的需要;(2)使用会话在多个请求之间建立状态(我们逐渐变得无会话);(3)仅支持使用generate命令db id作为标识符,其中用户可配置的代码通常是更好的标识符(对于与外部系统集成,通常,他们希望将标识符上载到我们的系统中,以便以后在api中使用,而不是相反)。这三者加上薄弱的文档和无益的错误消息,使得该API在没有帮助的情况下无法使用。

– Joeri Sebrechts
15年6月28日在13:18

#7 楼

您的同事所描述的是面向服务的体系结构。这可能是一种可扩展性强,可测试且理智的编码方式,但它实际上取决于您所做的工作。 >
可伸缩性

由于后端已解耦,因此前端仅是一系列模板,甚至是平面文件。从任何CDN提供服务,平面文件都非常快捷,便宜。可以将它们缩小并预编译为静态HTML,然后在数据客户端填充。例如,您可以在Go中重新制作。您可以零散地重建它,并在服务器之间分散负载。只要接口保持不变,就可以对技术进行抽象。MVC通常是从干净开始的,但实际上,控制器很少将注意力集中在单个资源上。控制器方法执行的操作越多,它们的可测试性就越低。

API绕开了这个问题。每个API调用都会提取资源并提供资源。干净且可测试。

保证分离的关注点

您的前端和后端已完全离婚。您可以将前端交给其他开发人员或设计师。这是将MVC带到另一个层次。我确定您不想放弃MVC。 SOA是MVC,但更多。

缺点

当然也有缺点。通常情况下,使用整体程序会更快。这可能是您习惯的。它可能更适合您的堆栈。您的工具可能已经过优化,可用于创建整体。

我认为,这些都不是特别好的理由,如果您愿意的话,不妨考虑进行重新设计。

评论


到目前为止,这是最明确的答案。

–托尼·恩尼斯(Tony Ennis)
15年7月4日在13:48

#8 楼

争论的重点不是您是否应该使用API​​,而是“ API”实际上是什么。使用设计的API的唯一替代方法是使用一个随机的代码混乱的API。您写道,API使事情“变得太灵活”,从而使事情变得难以管理。这表明对API是什么有一个彻底而彻底的误解。如果您和您的同事之间没有这种误解,那么您会因争论完全不同的事情而浪费了很多时间。根据定义,这是最灵活的选择。同样,根据定义,“随心所欲”仍然是API。
API的唯一工作就是消除灵活性。通过消除灵活性,好的API会鼓励用户以相似的方式做类似的事情。

当然,不好的API可能会提供过多或过少的灵活性,或者甚至同时提供两种灵活性。一个设计不佳的API可能比“一切皆有”方法更快地杀死一个项目。但是,最佳实践只是让有能力的程序员与您的应用程序一起开发和开发API。后退和
,例如某些代码可以获取当前用户,检查
用户是否具有管理员角色,获取该用户所属的公司
,获取其他成员列表,向他们发送所有电子邮件。这将
需要大量的API调用,或为您想要的特定
任务编写定制方法,而该定制方法的唯一好处是速度
,但不利之处在于它不够灵活。


像样的API所需的API调用数量可能为1。是的,它不灵活,但是为什么要使其灵活?

#9 楼

这里有很多很好的答案,所以我只添加自己的实现经验。

这是我的工作方式:


创建一个数据库访问层来处理所有/仅数据库交互(通常使用手动SQL进行速度和控制,不使用ORM)。插入,更新,删除,选择...
创建一个interfacevirtual class),以公开/实施我需要的API函数。他们将在实施后使用高度专业的DBAL函数来实现结果。它还可以帮助我在编译器级别强制执行A​​PI,从而确保Server + API实现具有内置的所有功能。
创建第二层以实现接口(这是实际的API)并强制执行安全性限制。您还可以在此处与外部API进行交互。
网站将直接使用第二层(以提高性能),而无需通过远程访问的API(例如SOAP,JSON)。
构建了一个独立的服务器来实现接口,并将第二层作为实际的远程访问API公开给外部台式机/移动客户端(非网站访问)。它所做的只是解码请求,编码响应以及管理/断开客户端。它还支持推回功能,以将其他连接的对等方生成的事件批量通知客户端(网站通常不需要的功能)。

因此,从技术上讲,API是第二层。您可以直接在网站上使用它,并通过服务器将其公开给远程客户端。代码被重用,并且没有可重用的代码块内联。 (按此规则生存和消亡,一切都很棒)帮助进行可维护性,测试...一切。在JSON上)。但是,如果网站在标记中呈现动态内容,则通过中间API会提高您的性能。网站需要快速!远程客户端访问可能会稍微慢一点。

PS:是的,随着更多的轮子一起工作,维护会稍微复杂一些,但从长远来看,它更容易。因此,如果您的项目打算生存一段时间并且有点复杂,请始终使用API​​。单独测试每个层也要容易得多。

评论


这听起来很酷,并且很有意义,尤其是在API类型的函数上放置一个接口。下次创建项目时,我将尝试进行此设置!

– NibblyPig
15年6月29日在9:25

#10 楼


他说我们的代码太紧密了。例如,如果我们也想要桌面应用程序,则将无法使用现有代码。


好吧?如果不是这样的话,那将是一个无关紧要的声明。

我要说的是,如果您打算在2015年构建一个新的应用程序,那么请认真考虑用户界面中涉及API而不是服务器生成的HTML页面。既有明确的成本,也有明确的利益。

但是,如果您有一个现有站点,但没有具体计划具有多个不同的接口(据我所知),那么他的评论就无关紧要。

#11 楼

简短版本:无论如何,您的控制器都有效地提供了API;尽管ASP.NET可能会掩盖这一点。路由是什么样的?

/sign_in
/sign_out
/beer
/beer/{beer_name}
/order
/order/{order_number}


在正常的网络应用中,可能会有一些辅助路由,例如:

/beer/new
/beer/{beer_name}/edit
/beer/{beer_name}/delete
/order/new
/order/{order_number}/edit
/order/{order_number}/delete


在Web API中不需要这些,因为它们是从HTTP方法推断出来的。

鉴于上述对称性,我认为这是一个非常令人信服的案例,说明您的API和Controller非常接近,以至于它们可能是同一件事。

进行一些挖掘之后,我确定这取决于您所使用的ASP.NET版本,这可能是您的状态您正在使用。较早的MVC 5和以前的版本缺少将两个实现完美地统一的约定和接口。在旧版本中,Web App return填充一个View,而API提供HttpResponse。但是,无论哪种情况,它们都会在语义上生成完全相同的响应。我没有找到任何适合此模型的ASP示例代码,但是我发现了一些具有相同模式的Rails代码。考虑从Diaspora项目获得“喜欢”的控制器。每个控制器方法都有一个由“资源约定”定义的路由,这些路由相当于API中的LCRUD。 。这与查找视图的约定结合在一起,完全统一了Web App和Web API。您还将注意到,并非所有方法实际上都提供每个响应(这很有意义,因为UI可能需要API不会提供的方法,反之亦然)。

这是一个阻抗不匹配的原因,因为ASP.NET有点想通了所有这些,而Rails已经接受了一段时间的对称性并使其非常清楚。

推测:您的同事可能是对是非,这取决于您使用的ASP版本。在旧的MVC版本下,API和App之间的差异可能确实使其成为“预先创建API”的“最佳实践”,因为ASP.NET的模型并未真正允许在那里良好的代码重用。 br />对于较新的版本,使用统一代码更有意义,因为使用统一控制器基类可以更轻松地重用代码。

无论哪种情况,Controller都是有效地使用API​​。

评论


这个问题已经死了,但是我不认为其他答案很清楚。 “您可能无法避免构建API。”答案几乎是现场的,被接受的答案围绕着同一问题跳动。但是两者都没有以我觉得很明确的方式专门针对ASP。

–杰森
2015年6月30日10:25



越多越多的人回答,他们有助于全面了解他人对此的看法。

– NibblyPig
15年7月1日在9:20

#12 楼

当我在2006年开始我的职业生涯时,.NET世界中这种类型的架构风靡一时。我参与了2000年代中期构想的3个独立项目,并在业务逻辑层和Web前端之间使用了Web服务。当然,这些天Web服务是SOAP,但它仍然是相同的体系结构。所谓的好处是能够切换前端或后端,甚至开发桌面程序。最终,YAGNI被证明是正确的。我从未见过这种情况。一直以来,我只看到以这种方式拆分项目的成本。我什至最终从一个项目中删除了Web服务(花了半年时间在做其他事情的同时逐步删除了它),整个团队都很高兴。我从没有尝试过这种方法,除非有非常具体的原因,否则我不会。尝试该架构的5年经验告诉我,我将不需要它,并且没有专家告诉我相反的说法可以说服我。只有我需要的项目才能做到这一点。

现在,我确实尽力在业务逻辑和控制器/演示者之间建立一层。例如,我有一个服务层,我从不公开可查询对象,对我所有的服务使用接口,并将它们注入到具有IoC的控制器中。如果我的体系结构中需要Web服务,我将能够以合理的价格引入它。我只是不想提前支付这笔费用。

我也很喜欢微服务的想法,但我的理解是微服务意味着垂直模块而不是水平层。例如,如果您正在构建Facebook,则聊天功能将是一项单独的服务,分别部署在其自己的服务器上,等等。我鼓励这种独立的服务。

#13 楼

第三方会用吗?是的,您应该这样做。

您打算在不久的将来重新使用它吗?是的,您应该成为第三方。拥有成文的-或可成文的-或可由第三方使用的API将为您提供可靠的可重用性和模块化。赶?不,您不应该。
比大多数方法和教师所预测和讲授的方法,事后重构更容易,更快捷。比起什么都不做,拥有有效的东西(即使内部设计不好,因为它可能并且会被重构)更重要。 (但具有令人难以置信的内部设计,哇)

由于某些原因,前端可能永远看不到今天的曙光?是的,您应该这样做。
我添加了这个原因是因为,这很经常发生在我身上。
至少我剩下的组件可以重用和重新分配。

#14 楼

这里有很好的答案。我将其发布为部分答案;作为评论也许会更好。但是,在众多帖子上贴相同的评论是不好的。

一个不能声明YAGNI的原因是未创建API的原因。 。因此,从第0天开始,就有两个使用API​​的应用程序:UI和测试套件。一个是给人类的,另一个是给机器的。它们一定是不同的。测试前端行为与测试后端行为大不相同。因此,技术以及可能的工具是完全不同的。该API允许将最佳工具用于这项工作。此外,借助API提供的分离功能,前端测试人员不必测试后端功能。

API还有助于使前端编码人员远离后端编码人员的担忧,反之亦然。这些是我们公司非常不同的技能。 API使我们能够专注于我们最强的地方。