Reputation: 45115
Picture a web service with two entities/objects, Room and Person where Room exposes the people in it via a Room.Occupants property collection. Each entity has a LastModified date.
If a new representation of a Room is PUT to the service with one occupant removed, should this change the LastModified on the Room?
The answer has ramifications for caching.
You could take the view that the entity that was actually modified was the Person, since its RoomId was set to NULL.
But a conditional request to http../rooms with the modified date would fail to reflect the change in the full representation of a Room, including its occupants.
If you take the view that the Room should have its LastModified changed, then you need to update the time-stamp on possibly two Rooms, whenever a Person is written to the service and its RoomId has changed.
And what if the Person has a collection of PersonalBelongings? This could get out-of-hand quickly, so should representations of objects exclude representing any associated collections to avoid all this complexity?
Luke
Upvotes: 2
Views: 275
Reputation: 402
In general, I would think that a change in one resource would imply that a previously fetched collection of contain said resource in now invalided. I would expect a new HEAD
and GET
request to fetch that collection would be have an updated Last-Modified
and ETag
headers.
I think @Darrel Miller is correct on the distinction between resources and domain entities. To me, this additionally means that their lifecycles are different and should be tracked separately. The way I see it, changes in one resource triggers changes in related collection resources.
With that said, how would you implement it without things getting out of hand? I have seen resource state stored in a database (separate from the entities). It could be as simple as a mapping of URLs to date stamps, but it really depends on your resource layout.
As for updating, I have seen use database triggers to update the resource states. Although it is straight-forward. I find it not a scalable and just plain messy. The strategy I like best is creating application events internal to the service implementation. This allows me to publish an event asynchronously whenever a resource changes. The event handlers update other resource states accordingly. I seen many ways to implement application events. It is similar to logging or recording history, so I consider application events a cross-cutting concern. So, I like to use AOP, but you should use anything that will work.
Upvotes: 1
Reputation: 142044
You will struggle with questions like this whilst you continue to define REST based systems using terms like entities and objects. REST deals in resources and representations and although this may seem like trivial wordplay, it can make a huge difference when dealing with issues like this.
The LastModified date that HTTP cares about is the last modified date of the representation. If the resource we are concerned about is /room/45/occupants
where its representation shows the people in the room and someone just left the room, then I would say that there is no doubt that the representation should have a new LastModfied value.
The details of what happens to the room and person entities that are hidden behind the REST interface is a completely separate concern.
The design of resources and representations is different from the design of your domain entities in the same way that your domain entities are different than your database structure used to store those entities.
Upvotes: 3
Reputation: 29021
You end here with the classical chicken and egg problem. You have to decide if Persons have life "outside" a room. If this is the case, then you can find Persons with a NULL RoomId, and, Persons would live outside rooms, so Room.occupants will hold references (URLs) to Persons (instead of containing the persons).
If Persons only exist within rooms, then you cannot have Persons with NULL RoomIDs, because all the collection management for Persons is made inside Rooms (add new Person, remove one) and can effectively modify/update the LastModified, occupannt number, etc.
You then have to decide who manage transactions. In the second case (Persons don't live outside Rooms), the Room itself stores all the state, so no transactions are needed to update the state (you just remove or add a Person and the state of the room is changed).
In the first case, note that Rooms and Persons are inter-related. If transactions are managed by the client, the client must ensure that it always executes the steps neccessary when deleting a Person. That is:
If state management is performed in the server, deleting a Person makes the Room to be modified. This forces invalidating the Room copies when a Person is deleted, and it is up to the server to cascade the change to Person to the Room (that is, updating its LastChanged). This is hard-coded logic in the server.
Upvotes: 1
Reputation: 192467
It's up to you to define what makes sense in your domain.
When someone leaves a room, does that "modify" a room? I don't think there is a single correct answer, though the intuition that flows from the metaphor is that no, the room is not modified when someone enters or leaves. That is the common sense understanding of the way rooms act in the real world, though I understand you are using room and person only as example types, and the problem in your actual scenario may not be so clear cut.
The key thing is to simply be consistent.
A first step is to say "There is no PUT allowed on a room that exists." You could make it impossible to "Add rooms" via PUT, which would eliminate the possibility of your initial scenario, PUTing a room with one fewer person in it. To "move" a person from one room to the next you might POST to a person with a given ID.
On the other hand, you may want to POST a message to a room in order to update the list of occupants in the room. In which case you may not wish to allow the converse POST to move a person.
Which option you choose is up to you - as long as it's consistent and sensible, it's fine.
Another reasonable step is to appropriately name "LastModified". In your Room and Person scenario, a more appropriate name might be "LastContentsChange" or "LastOccupantsChange."
The reasonable question that follows is, does my app NEED to know the last time the set of occupants of a room has changed? And of course that is a question only you can answer.
So you iterate - you make some provisional design decisions, then think about the implications when you use that design in action. Then you revisit and change some of the design decisions, and make some new tests. And so on.
It may help to be able to rapidly prototype your design, using a simplified backing store, just to try out ideas.
Upvotes: 3