Search code examples
design-patternsapi-design

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


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.


Solution

  • 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?