Description
Providing create/update/delete callbacks in IResourceDefinition
(to implement business rule invariants) reduces the need to override resource services and repositories. We'd like to support both fire-and-forget, as well as the Transactional Outbox microservices pattern.
Fire-and-forget requires an async callback after the change has been committed. Transactional Outbox requires a callback before committing changes, so it can inject DbContext
and add a record to the Outbox table, which gets committed along with the resource write in a single transaction. Obviously this can only work when using GUIDs, so the assigned resource ID on creation is known upfront.
These callbacks should remove the need for developers to inspect ITargetedFields
and it should enable them to modify both exposed and non-exposed resource properties and relationships. Finally, this should not break the resource change detection mechanism, which determines whether HTTP 200 or 204 is returned.
Create resource (POST)
For create-resource, we need to provide an async callback on an empty resource, to enable setting required relationships upfront or side-load related resources to make it possible to apply field assignments from the request. This is also the place to assign default values to fields. Another callback after the targeted fields from request have been applied (but before changes are saved) is needed to allow setting fields such as LastModifiedAt, possibly overwriting any values from the incoming request. This is also the place to create an Outbox record. We need to provide another callback after changes have been saved successfully for fire-and-forget.
Update resource (PATCH)
For update-resource, we need a callback taking the database version of the resource, which enables to change fields before the changes from the request are applied. This callback can throw when the resource is soft-deleted or archived. After the changes from request have been applied (but before changes are saved), another callback would enable the developer to overwrite data from the request (such as LastModifiedAt) and create the Outbox record. Finally, we need an async callback after changes were saved successfully to send out a notification to the service bus.
Delete resource (DELETE)
For efficiency, we don't fetch the full resource to be deleted. An async callback before executing the delete statement takes the resource ID, so it can fetch the resource itself and throw if deletion is not allowed based on its fields (for example, a non-closed support request). Note this does not solve the problem that a soft-delete needs to execute an UPDATE statement instead of DELETE (you'll still need to overwrite behavior in resource repository for that). After the resource has been deleted, a callback should enable fire-and-forget notification.
Relationship updates (POST/PATCH/DELETE)
Don't provide anything in this area (yet). It quickly gets very complicated and we need a better understanding of use cases to get it done right. We currently have several optimizations in place to fetch as few data as possible, which we may need to reconsider for a consistent experience at that time.
Pre-processing incoming resources (POST/PATCH)
Similar to the next one. Provide a callback to pre-process incoming resources from request body, invoked from the deserializer.
Post-processing returned resources (GET/POST/PATCH)
Stretch goal, initially not in scope for this proposal. It would probably be best to invoke such callbacks from the serializer, after request processing has completed. It would enable to change field values, like scrub sensitive data or make all country names uppercase. Any changes done here only affect what is sent to the client. Changes are not saved and don't influence resource change detection (HTTP 200/204). It also does not enable to add/remove resources in the collection of returned resources.