一种API方法做两件事;在创建API时这是一个好习惯吗?

时间:2013-12-13 04:45:56

标签: design-patterns api-design

我和我的同事对我们服务的设计有点意见不一,我需要社区帮助做正确的事情。

上下文:我们正在创建一项服务,允许画家/插图画家将图像存储在云端。一组图像生成图像包。

场景:我们正在尝试添加逻辑以将艺术家与图片包相关联。我们目前已经有了一个API,允许我们创建图像包(只不过是指向图像的链接,带有名称和desc)。我们没有用于创建艺术家的API,也没有将艺术家与图像包相关联的API。

他的观点 :在调用createImagePack时,添加以下参数[artist_id,artist_name,artist_desc]。如果artist_id为“null”,我们将使用artist_name和artist_desc在系统中创建新的艺术家,并自动将他/她分配给新创建的图像包。然后,API将使用image_pack_id和artist_id进行响应。

我的观点 :上述方法混合了问题,增加了管理和更新createImagePack API的风险,打破了“API应该做什么”的一般经验法则它被命名为“。 [我们可以更新名称来表示createImagePackAPIAndCreateArtistIfArtistIDNotNull,但这也有一些代码味道]。我们应该将这两个调用分开并使它们成为独立的API并创建一个外观来支持一次性创建艺术家和图像。

我在分析这个吗?他们的文献是关于什么是最好的行动方案的好文章?

UPDATE :我们的系统中确实有“用户”的概念,因此另一种方法是创建一个关联表,我们将user_id与ARTIST_PACK_MAP表中的pack_id相关联。这避免了为艺术家创建一个单独的实体,并松散地将我们与我们的“用户”概念联系起来,而这个概念没有任何强烈的打字。

3 个答案:

答案 0 :(得分:2)

系统及其API应

  • 明显
  • 可预测的
  • 沟通
这例如在Evans的“领域驱动设计”(例如第10章,“柔性设计”)中描述。我认为这些想法甚至超出了DDD的范围。

功能应该是无副作用,这意味着他们应该完成一件事。所以我认为你的函数“createImagePack”不应该具有创建艺术家的明显副作用。

通过意图揭示界面可以预测您的API。

你的API究竟是什么?在我看来,您应该为艺术家,图像,图像包提供单独的域模型类...如果有一种方便的方法可以在更高的层次上抽象处理,那么它应该是 facade 。但是命名应该清楚地传达它的作用。

也许你甚至应该退一步看看你的应用程序的域名。如果没有现有的艺术家,您的应用程序可能永远不会处理图像或图像包。也许它应该更像[艺术家] - > [创建图像包]。这可能比其他方式更强烈明显

  

我在分析这个吗?

我不这么认为。

  

他们的文献是关于什么是最好的行动方案的好文章?

你可以看看DDD的想法。即使您没有以DDD方式设计应用程序。

另一种可能性是看待测试驱动的开发。如果您将应用程序设计为可测试性,那么您可能希望很好地分离关注点 - 没有副作用。

  

Fendy:如果你将API分成两个调用,如果第二个webservice在第一次成功时是错误的,你如何处理?

他是否真的在网络服务级别讨论API?艺术家来自哪里?我认为当画家使用这种云服务时,他就是艺术家,当他使用系统进行身份验证时,应该已经有了艺术家实体吗?

答案 1 :(得分:1)

我认为在一种方法中混合关注点并不是一个好主意。一种方法应该做一件事(例如createImagePack,createArtist,associateImagePack,deleteArtist等)。但是,您可以将对逻辑相关方法的调用组合到一个方法调用中,以使用Façade模式创建复合功能。

在你的façade类中,你将有一个如下定义的方法:

 public Artist createImagePackAndArtist(String artist_name, String artist_desc){        
     ImagePack imagePack = new ImagePack();
     Artist artist = new Artist(artist_name, artist_desc);
     artist.associateImagePack(imagePack);
     return artist;
 }

在这里,您将功能/方法调用组合到一个方法中。

从返回的对象Artist中,您可以调用方法返回艺术家的ImagePack对象,然后返回imagePack_id,Artist对象的artist_id等。

外观的目的是提供方便的方法。您可以创建另一种方法,将新图像包添加到已存在的艺术家。

  public void createImagePackForArtist(Artist artist){        
     ImagePack imagePack = new ImagePack();
     if (artist != null) {        
           artist.associateImagePack(imagePack);
     } else {
           // throw ArtistIsNullException
     }
 }

理想情况下,在尝试关联图像包之前,您会检查艺术家是否存在。这将发生在外立面之外。

另请参阅工厂模式。您可以实现此而不是自己创建对象。还要看一下单例模式。外墙和工厂通常是单身人士。

答案 2 :(得分:1)

API设计主要取决于您的业务流程。如果API'服务'仅在内部使用(如mdo所述),那么我同意它应该分开并使用类似Unit of Work模式的事务处理。它具有灵活性,可维护性和可重用性的优点。

公开为Web服务

但是,当您的API暴露在外部(或用作webservice用于ajax调用)时,它就变得很重要了。当用作ajax调用时,它会改变您的业务流程,因为用户现在是否需要在创建artist实体之前创建image pack实体。

在某些过程中,它可能是一件坏事(例如在博客中评论,如果你需要事先创建一个帐户,而不是足够灵活),但在其他情况下这是一个要求(Facebook或SO需要有发布前的帐户)。我相信图像共享器(例如devianart),创建一步图像包(可能需要电子邮件)更好。

作为第三方图书馆曝光

当作为第三方库公开时(在开源应用程序或合作伙伴使用时),您必须考虑两件事。

首先,您无法再控制业务规则(可以在没有图像包的情况下创建艺术家)。其次,您需要提供部分成功更新的回滚功能(可能使用unit of works),因为合作伙伴需要它。

<强> TL; DR

更改内部使用的API,分离关注/进程是好的,因为它可以在内部处理。当API暴露在外部时,需要考虑事先考虑(例如回滚能力),因为外部方可能无法改变流程。