One API method doing two things; is that good practice when creating APIs? [closed]

StackOverflow https://stackoverflow.com/questions/20559272

  •  01-09-2022
  •  | 
  •  

Pergunta

My colleague and I have a bit of a disagreement on the design of our service and I needed help from the community on what is the correct thing to do.

Context: We are creating a service that allows painters/illustrators to store images in the cloud. A group of images makes an image pack.

Scenario: We are trying to add logic to associate an artist with an image pack. We currently already have an API that allows us to create image packs (nothing more than a link to images, with a name and desc). We don't have an API to create an artist, nor one to associate artists to image packs.

His Perspective: In the call to createImagePack add the following parameters [artist_id, artist_name, artist_desc]. If artist_id is "null" we will use the artist_name and artist_desc to create a new artist in the system and automatically assign him/her to the newly created image pack. The API would then respond back with an image_pack_id and artist_id.

My Perspective: The above approach is mixing concerns, adds risk to managing and updating the createImagePack API and breaking the general rule of thumb of the "API should do what it is named". [We can update the name to say createImagePackAPIAndCreateArtistIfArtistIDNotNull, but that has a few code smells as well]. We should separate the two calls and make them independent APIs and create a facade to support the creation of the artist and image in one go.

Am I over analyzing this? Is their literature of good write-ups on what is the best course of action here?

UPDATE: We do have the concept of a "user" in our system, so an alternative approach is to create an association table, where we associate a user_id, to pack_id in a ARTIST_PACK_MAP table. Which avoids creating a separate entity for an artist and loosely ties us with our "user" concept, which does not have any strong typing.

Foi útil?

Solução

Systems and their APIs should be

  • obvious
  • predictable
  • and communicative

This is for example described in Evans' "Domain-driven design" (e.g. chapter 10, "Supple design"). I think these ideas are a good rationale even out of the scope of DDD.

Functions should be side-effect-free, which means that they should do exactly one thing. So I think your function "createImagePack" should not have the unobvious side effect of creating an artist.

Your API could be predictable by having intention-revealing interfaces.

What is your API anyway? Seems to me that you should have separate domain model classes for artists, images, image packs ... If there is a convenience method that abstracts the handling on a higher level it should be a facade. But the naming should clearly communicate what it does.

Perhaps you should even go one step back and look at your application's domain. Maybe your application should never do any handling of images or image packs without an existing artist. Perhaps it should be more like [artist] -> [create image pack]. This could be more obvious than the other way round!

Am I over analyzing this?

I don't think so.

Is their literature of good write-ups on what is the best course of action here?

You could have a look into DDD ideas. Even if you don't design your application the DDD way.

Another possibility was to look at test-driven development. If you design your application for testability, you will likely want to have a good separation of concerns, too -- without side-effects.

Fendy: If you separate the API as two calls, how can you handle if the second webservice is error while the first one success?

Is he really talking about an API at the webservice level? Where do the artists come from? I think when a painter uses this cloud service he is the artist and when he authenticates with the system there should already be an artist entity?

Outras dicas

I don't think that its a good idea to mix concerns in one method. One method should do one thing (e.g. createImagePack, createArtist, associateImagePack, deleteArtist etc). However what you can do it combine the calls to logically related methods into one method call to create composite functionality using the Façade pattern.

In your façade class you will have a method defined as follows:

 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;
 }

Here you are combining functionality/method calls into one single method.

From the returned object Artist you can call methods to return the ImagePack object for the artist, then the imagePack_id, the artist_id from the Artist object etc.

The object of the façade is to provide convenient methods. You could create another method that adds a new image pack to an artist that already exists.

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

Ideally you would check to see if the artist exists before attempting to associate the image pack. This would happen outside the façade.

Also have a look at the Factory pattern. You could implement this instead of creating the objects yourself. Also take a look at the singleton pattern. Façades and factories are often singletons.

The API design depends mainly on your business process. If the API 'service' only being used internally (as stated by mdo), then I agree it should be separated and use something like Unit of Work pattern for transaction handling. It has benefits of flexibility, maintainability and reusability.

Exposed as Webservice

However when your API is exposed externally (or used as webservice for ajax call), it become matters. When used as ajax call, it changed your business process as whether the user now need to create artist entity before creating an image pack entity.

In some process it may be a bad thing (for example commenting in blogger, if you need to create an account beforehand, than it is not flexible enough), but in other it is a requirement (facebook or SO need to have account before posting). I believe in image sharer (such as devianart), 1 step image pack creating (maybe with e-mail required) is better.

Exposed as 3rd Party Library

When being exposed as 3rd party library (when in open source apps or partners use), there are 2 things that you must consider.

First is, you cannot control the business rule anymore (an artists can be created without image pack). Second, you need to provide the rollback ability for partially success update (maybe with unit of works), since somewhere the partner will need it.

TL;DR

Changing API used internally, separating the concern / process is good, since it can be handled internally. When the API are exposed externally, there are some consideration to think beforehand (such as rollback ability) since the external party may not have the ability to change the process.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top