RFC: Separate Concerns of JsonApiContext
See original GitHub issueStart Date: N/A Status: WIP RFC PR: N/A
Summary
Refactor the JsonApiContext
into classes with singular responsibilities.
Motivation
The JsonApiContext
is a god container whose members are set in various places during the request cycle. Because of the large set of responsibilities this class takes on, it can make testing difficult. Its role, in general, is to ensure ambient data is available whenever necessary.
This was more of an issue during the early stages of development when the architecture was still in flux. Now that the internal APIs are solidifying, we can begin separating this class into single responsibility implementations.
Note: The primary benefit that still exists of the JsonApiContext
is that it reduces the number of constructor dependencies required for each class.
Detailed Design
TODO: map all the consumption points and identify separation strategies
Controllers (#255)
ApplyContext<TController>
. This method is responsible for:
- Setting the controller type on the context
- Move into ActionFilter
- Setting
RequestEntity
- Move into ActionFilter
- May create a scoped
IResourceRequest
that providesGet(/* ... */)
andSet(/* ... */)
methods to replace this functionality.
- Parsing Query Params
- Move into ActionFilter
- Setting the list of IncludedRelationships
- Determining if it is a RelationshipPath
- Creating a
PageManager
We should be able to remove the direct dependency by moving the functionality into an ActionFilter without any other modifications. However, the functionality defined above still needs to be factored out of the context where possible.
Deserializer
-
Sets
RequestEntity
. This is redundant with the controller and we should be able to remove this step. https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/e7c65db8cf9bcd064eb7001703b8a2238095ee4f/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs#L107-L108 -
Sets
IsBulkOperationRequest
-
Sets
DocumentMeta
: we should consider storing the deserialized document in the request somewhere -
Sets
AttributesToUpdate
-
Depends on
_jsonApiContext.Options.SerializerSettings
: we could directly depend onJsonApiOptions
instead -
Sets
RelationshipsToUpdate
Proposed Approach:
- Introduce
JsonApiDeSerializerFactory
that produces either:JsonApiOperationsDeserializer
JsonApiDeSerializer
(consider aliasing to a more descriptive name)
- Optionally depend on
JsonApiOptions
directly - Depend on the
ContextGraph
directly - Extract out a
DocumentRequest
type that includes:- The original
Documents
orDocument
- The attributes to update
- The relationships to update
- The original
public JsonApiDeSerializer(ContextGraph contextGraph, JsonApiOptions options = null)
{
_contextGraph = contextGraph;
_options = options ?? JsonApiOptions.Default;
}
Phased Implementation
The intent is to make changes in such a way that users of the library can gradually depend on the new structure without forced breaking changes. The rollout of this change will span 4 releases as outlined below:
- Allow internal consuming classes to depend on the new interfaces in a non-breaking way. For example, if we can find a way to remove the requirement that the controller call
ApplyContext<T>
, then there is no need for the controller to even depend on theJsonApiContext
. In this case, we would keep the original constructor but provide an implementation that does not require the context:
public BaseJsonApiController(
IJsonApiContext jsonApiContext,
IGetAllService<T, TId> getAll = null,
/* ... */
) { /* ... */ }
public BaseJsonApiController(
IGetAllService<T, TId> getAll = null,
/* ... */
) { /* ... */ }
- Obsolete the APIs dependent upon
IJsonApiContext
.
[Obsolete(/* ... */, error: false)]
- Publish a breaking change release (v3 ?) that sets the
ObsoleteAttribute
error to true
[Obsolete(/* ... */, error: true)]
- Drop the APIs in the following major release
App vs. Request vs. Operations
This needs its own issue
The JsonApiContext
stores three kinds of information
- Application: state that does not change for the life of the application
JsonApiOptions
ContextGraph
- Request: state that applies to the entire request
BasePath
IsRelationshipPath
IsBulkOperationRequest
ControllerType
DocumentMeta
- Operational: per-operation data
IncludedRelationships
AttributesToUpdate
RelationshipsToUpdate
HasManyRelationshipPointers
HasOneRelationshipPointers
QuerySet
- …
Issue Analytics
- State:
- Created 5 years ago
- Comments:15 (15 by maintainers)
Top GitHub Comments
I think that this will open up the project to a LOT mroe people!!
On Thu, Oct 3, 2019 at 3:33 PM Maurits Moeys notifications@github.com wrote:
@wayne-o that’s correct and would be my suggestion for now. #241 will make this much better once it is implemented. Any suggestions you have are more than welcome.