Tip 4: Think of different states as different resources #4
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The last custom actions we have are
PodcastsController@publish
andPodcastsController@unpublish
.Here are the endpoints:
At first glance you might think:
"All we're doing is updating the
published_at
column, let's usePodcastsController@update
for this."But we already have an
update
action, and as we've already discussed, trying to cram two actions into one is a recipe for complexity.So how can we model "publish" as it's own standard REST action?
If we ask ourselves the same question we did in the previous refactoring:
"After publishing a podcast, what do I have now that I didn't have before?"
...one answer that comes to mind is a "published podcast."
So what would it look like to model "publishing a podcast" as "creating a published podcast"?
✅ Create a dedicated
PublishedPodcastsController
In these situations, it can often be helpful to think of a resource in a certain state as it's own independent resource.
If we are creating a new "published podcast", the standard endpoint structure look like this:
As before, we lose the
{id}
route parameter here, so we'll need to pass that through the request body.This feels pretty natural when you think of the request body as being the "raw materials" needed to create the resource. When you are creating a published podcast, the raw materials needed are an existing unpublished podcast, and the best way to represent that entity is with it's identifier.
Here's what our new controller action would look like:
One thing to note here is the return value. Previously we were returning an empty
204
response, but now that we're "creating" a new resource, returning the model seems more appropriate.Modelling
unpublish
Just like
subscribe
, ifpublish
becomes creating a published podcast, thenunpublish
could become destroying a published podcast.This one probably requires the biggest mental leap of any of our refactorings so far, but I promise that in practice it doesn't end up feeling as weird as it first sounds.
Here's what the endpoint would become:
"Published podcasts" don't have any sort of special unique ID; instead their ID is the same as a regular podcast's ID, so that's what our
{id}
parameter will represent here.Here's what the full controller would look like:
After this refactoring, here's where we stand:
PodcastsController
, with 7 standard actions and no custom actionsEpisodesController
, with 4 standard actions and no custom actionsPodcastEpisodesController
, with 3 standard actions and no custom actionsPodcastCoverImageController
, with 1 standard action and no custom actionsSubscriptionsController
, with 2 standard actions and no custom actionsPublishedPodcastsController
, with 2 standard actions and no custom actionsThat's 6 controllers with an average of ~3 methods per controller. Small, clean, and simple!