Conditionally disable Apollo cache normalization for certain usage of a type? - caching

I have a situation, using Apollo InMemoryCache on a React client, where I'd like to be able to instruct Apollo not to use cache normalization for certain nodes in the graph without having to disable caching entirely for that type. Is this possible?
To better explain what I mean: Say that I have an entity Person, that I generally want Apollo to use cache for, but I also have an endpoint called PersonEvent that has these two fields:
old: Person!
new: Person!
This returns two historic snapshots of the same person, used for showing what changed on a certain event in time. The problem is that with cache normalization turned on for Person, the cache would interpret old and new as being the same instance since they have the same id and __typename, and then replacing it with the same reference.
I know it is possible to configure Apollo not to normalize objects of a certain type, using the config code below, but then all caching of Person objects is disabled, and that's not what I want:
typePolicies: {
Person: {
keyFields: false
}
}
So my question is: What would be the best practice way to handle this situation? I think it's kind of a philosofical question to it as well: "Is a snapshot of a person, a person?". I could potentially ask the backend dev to add some sort of timestamp to the Person entity so that it could be used to build a unique ID, but I also feel like that would be polluting the Person object as the timestamp is only relevant in case of a snapshot (which is an edgecase). Is this a situation that should generally be solved on the client-side or the server-side?
Given that the graph is as it is, I'd like to only instruct Apollo not to cache the old/new fields on PersonEvent, but I haven't found a way to achieve that yet.

To get philosophical with you:
Is a snapshot of a person, a person?
I think you're answering your question by the problem you're having. The point of a cache is that you can set a value by its ID and you can load that value by its ID. The same can be said for any entity. The cache is just a way of loading the entity. Your Person object appears to be an entity. I'm guessing based on your conundrum that this is NOT true for this snapshot; that it isn't "an entity"; that it doesn't have "an ID" (though it may contain the value of something else's id).
What you are describing is an immutable value object.
And so, IMO, the solution here would be to create a type that represents a value object, and as such is uncacheable: PersonSnapshot (or similar).

Related

Entity/Domain purety dilemma in the clean architecutre/Domain driven design

Im working on a eCommerce system in which I try to implement the clean architecture.
But currently Im stuck a little bit.
So I have a use case called: CreateItemUseCase in which I create a Item (alias product) for the shop.
In this use case I call a method (createItemEntity()) of a Entity called ItemEntity.
This method creates just a data object with data like:
userId
itemTitle
itemDescription
...
So now I need another method in the ItemEntity which validates the userId.
To create a Item the user needs to have a userId so the method in the ItemEntity would be called:
validateUserId()
This method should check if the user has a userId in the database and if not the Item creation would be imposible.
Now my question:
How do I validate the userId?
Should I have the validateUserId() method take a array as a parameter, In which all the User Id´s are saved... something like this:
validateUserId(toBeValidated: Int, allUserIds: Array[Int])
{
// loop through the allUserIds to see if toBeValidated is in there ...
}
Or should I query the data in the method (which Im pretty sure, would violate the dependencie rule) like this:
validateUserId(toBeValidated: Int)
{
// get all user id´s through a query, and check if toBeValidated is in there ...
}
Or should I do it completly different?
In general, entities should only contain logic that is operating on information (data) that is within the entity's scope. Knowing how to query if a user with a certain user id exists or not is not in the scope of the item entity.
I think your motivation to keep all the logic for validation together is reasonable but on the other hand you should not introduce infrastructure dependencies (like talking to the database or user repository) to the entity. Knowing how to query if a user with a certain user id exists or not is not in the scope of the item entity.
Or should I query the data in the method (which Im pretty sure, would violate the dependencie rule) like this
Exactly, that's why it's usually best trying to avoid that to keep entities free from such dependencies. Introducing such dependencies can easily get out of hand and also increase complexity for testing such entities. If you need to do that it should be a very thought decision that justifies that.
Should I have the validateUserId() method take a array as a parameter, In which all the User Id´s are saved... something like this
This is not such a bad idea in general, because you would not make the entity dependent on infrastructure and provide the entity with all the data it needs for decision making. But on the other hand now you can run into another problem: bad performance.
Now you would retrieve all user ids everytime you create an item. If you would do the check for the user's existence somewhere else this can be optimized much better.
I suggest to ask the user repository beforehand if the user exists prior to performance the entity creation including all the other potentially required validations inside item entity that make sense there. The user repository could have a query that optimizes for just checking for the existence of this user by id.
In case these two operations (asking for the user's existence and creating the new item) only happen at one place of the application I'd be pragmatic and perform the user existence check directly in the use case. If this would occur from different places in your application you can extract that logic into a separate (domain) service (e.g. item service) which deals with the repetitive flow operations working with the user repository and item entity.
What you are dealing here with is a trade-off decision between domain model purity, domain model completeness and performance considerations. In this great blog this is named the Domain-Driven Design Trilemma. I suggest going through the reasoning in the article, I'm pretty sure it will help you coming to a final decision.
I think this is one of side case of what we call Business Gerunds
Details: https://www.forbes.com/sites/forbestechcouncil/2022/05/19/10-best-practices-for-event-streaming-success/
If Item has to validate the user, just see what common attributes are there between entities and who is responsible for change of those, and then a segregation can be done in DDD representation, and using a composite via transaltion, outside world entities can exist as the same

How do i 'destroy all' a given Resource type in redux-saga?

I'm new to Redux-Saga, so please assume very shaky foundational knowledge.
In Redux, I am able to define an action and a subsequent reducer to handle that action. In my reducer, i can do just about whatever i want, such as 'delete all' of a specific state tree node, eg.
switch action.type
...
case 'DESTROY_ALL_ORDERS'
return {
...state,
orders: []
}
However, it seems to me (after reading the docs), that reducers are defined by Saga, and you have access to them in the form of certain given CRUD verb prefixes with invocation post fixes. E.g.
fetchStart, destroyStart
My instinct is to use destroyStart, but the method accepts a model instance, not a collection, i.e. it only can destroy a given resource instance (in my case, one Order).
TL;DR
Is there a destroyStart equivalent for a group of records at once?
If not, is there a way i can add custom behavior to the Saga created reducers?
What have a missed? Feel free to be as mean as you want, I have no idea what i'm doing but when you are done roasting me do me a favor and point me in the right direction.
EDIT:
To clarify, I'm not trying to delete records from my database. I only want to clear the Redux store of all 'Order' Records.
Two key bit's of knowledge were gained here.
My team is using a library called redux-api-resources which to some extent I was conflating with Saga. This library was created by a former employee, and adds about as much complexity as it removes. I would not recommend it. DestroyStart is provided by this library, and not specifically related to Saga. However the answer for anyone using this library (redux-api-resources) is no, there is no bulk destroy action.
Reducers are created by Saga, as pointed out in the above comments by #Chad S.. The mistake in my thinking was that I believed I should somehow crack open this reducer and fill it with complex logic. The 'Saga' way to do this is to put logic in your generator function, which is where you (can) define your control flow. I make no claim that this is best practice, only that this is how I managed to get my code working.
I know very little about Saga and Redux in general, so please take these answers with a grain of salt.

What is a "fully-hydrated User object" in the context of GraphQL? [duplicate]

When someone talks about hydrating an object, what does that mean?
I see a Java project called Hydrate on the web that transforms data between different representations (RDMS to OOPS to XML). Is this the general meaning of object hydration; to transform data between representations? Could it mean reconstructing an object hierarchy from a stored representation?
Hydration refers to the process of filling an object with data. An object which has not yet been hydrated has been instantiated and represents an entity that does have data, but the data has not yet been loaded into the object. This is something that is done for performance reasons.
Additionally, the term hydration is used when discussing plans for loading data from databases or other data sources. Here are some examples:
You could say that an object is partially hydrated when you have only loaded some of the fields into it, but not all of them. This can be done because those other fields are not necessary for your current operations. So there's no reason to waste bandwidth and CPU cycles loading, transferring, and setting this data when it's not going to be used.
Additionally, there are some ORM's, such as Doctrine, which do not hydrate objects when they are instantiated, but only when the data is accessed in that object. This is one method that helps to not load data which is not going to be used.
With respect to the more generic term hydrate
Hydrating an object is taking an object that exists in memory, that doesn't yet contain any domain data ("real" data), and then populating it with domain data (such as from a database, from the network, or from a file system).
From Erick Robertson's comments on this answer:
deserialization == instantiation + hydration
If you don't need to worry about blistering performance, and you aren't debugging performance optimizations that are in the internals of a data access API, then you probably don't need to deal with hydration explicitly. You would typically use deserialization instead so you can write less code. Some data access APIs don't give you this option, and in those cases you'd also have to explicitly call the hydration step yourself.
For a bit more detail on the concept of Hydration, see Erick Robertson's answer on this same question.
With respect to the Java project called hydrate
You asked about this framework specifically, so I looked into it.
As best as I can tell, I don't think this project used the word "hydrate" in a very generic sense. I see its use in the title as an approximate synonym for "serialization". As explained above, this usage isn't entirely accurate:
See: http://en.wikipedia.org/wiki/Serialization
translating data structures or object state into a format that can be stored [...] and reconstructed later in the same or another computer environment.
I can't find the reason behind their name directly on the Hydrate FAQ, but I got clues to their intention. I think they picked the name "Hydrate" because the purpose of the library is similar to the popular sound-alike Hibernate framework, but it was designed with the exact opposite workflow in mind.
Most ORMs, Hibernate included, take an in-memory object-model oriented approach, with the database taking second consideration. The Hydrate library instead takes a database-schema oriented approach, preserving your relational data structures and letting your program work on top of them more cleanly.
Metaphorically speaking, still with respect to this library's name: Hydrate is like "making something ready to use" (like re-hydrating Dried Foods). It is a metaphorical opposite of Hibernate, which is more like "putting something away for the winter" (like Animal Hibernation).
The decision to name the library Hydrate, as far as I can tell, was not concerned with the generic computer programming term "hydrate".
When using the generic computer programming term "hydrate", performance optimizations are usually the motivation (or debugging existing optimizations). Even if the library supports granular control over when and how objects are populated with data, the timing and performance don't seem to be the primary motivation for the name or the library's functionality. The library seems more concerned with enabling end-to-end mapping and schema-preservation.
While it is somewhat redundant vernacular as Merlyn mentioned, in my experience it refers only to filling/populating an object, not instantiating/creating it, so it is a useful word when you need to be precise.
This is a pretty old question, but it seems that there is still confusion over the meaning of the following terms. Hopefully, this will disambiguate.
Hydrate
When you see descriptions that say things like, "an object that is waiting for data, is waiting to be hydrated", that's confusing and misleading. Objects don't wait for things, and hydration is just the act of filling an object with data.
Using JavaScript as the example:
const obj = {}; // empty object
const data = { foo: true, bar: true, baz: true };
// Hydrate "obj" with "data"
Object.assign(obj, data);
console.log(obj.foo); // true
console.log(obj.bar); // true
console.log(obj.baz); // true
Anything that adds values to obj is "hydrating" it. I'm just using Object.assign() in this example.
Since the terms "serialize" and "deserialize" were also mentioned in other answers, here are examples to help disambiguate the meaning of those concepts from hydration:
Serialize
console.log(JSON.stringify({ foo: true, bar: true, baz: true }));
Deserialize
console.log(JSON.parse('{"foo":true,"bar":true,"baz":true}'));
In PHP, you can create a new class from its name, w/o invoke constructor, like this:
require "A.php";
$className = "A";
$class = new \ReflectionClass($className);
$instance = $class->newInstanceWithoutConstructor();
Then, you can hydrate invoking setters (or public attributes)

'Existing Entity' constraint

I'm reading some data from an excel file, and hydrating it into an object of class A. Now I have to make sure that one of the fields of the data corresponds to the Id of a specific Entity. i.e:
class A{
protected $entityId;
}
I have to make sure that $entityId is an existing id of a specific entity (let's call it Foo). Now this can be achieved using the choice constraint, by supplying the choices option as all of the existing ids of Foo. However this will obviously cause a performance overhead. Is there a standard/better way to do this?
I'm a bit confused about what you are doing, since you seem to talk about Excel parsing, but at the same time you mention choices, which in my opinion relate to Forms.
IMO you should handle directly the relationship to your entity, instead of only its id. Most of the time it is always better to have directly the related entity as attribute of your class A than only the id, and Symfony manipulates such behaviours pretty well.
Then just have your Excel parser do something like this:
$relatedEntity = $this->relatedEntityRepository->find($entityId);
if (!$relatedEntity) {
throw new \Exception();
}
$entity->setRelatedEntity($relatedEntity);
After doing this, since you were talking about Forms, you can then use an EntityType field which will automatically perform the request in database. Use query_builder if you need to filter the results.

Best practice with coding system values

I think this should be an easy one, but haven't found any clear answer, on what would the best practice be.
In an application, we keep current status of an order (open, canceled, shipped, closed ...).
This variables cannot change without code change, but application should meet the following criteria:
status names should be easily displayed in different languages,
application can search via freetext status names (like googling for "open")
status_id should be available to developer via enum
zero headache when adding new statuses
Possible ways we have tackled this so far:
having DB table status with PK(id, language_id) and a separate enum which represents this statuses in an application.
PROS: 1.,2.,3. work out of the box, CONS: 4. needs to run update script on every client installation, SQL selects can become large and cumbersome, when dealing with a lot of code tables
having just enum:
PROS: 3.,4. CONS: 1.,2. is a total nightmare
having enums, which populate database tables on each start of an application:
PROS: 1.,2.,3.,4. work CONS: some overhead on application start, SQL select can become large and cumbersome, when dealing a lot code tables.
What is the most common way of tackling this problem?
Sounds like you summarized it pretty good yourself, and comparing the pros/cons points towards #3. Just one comment when you implement #3 though:
Use a caching mechanism (even a simple HashMap!) plus adding the option to refresh the cache - will ease your work when you'll want to change values (without the need to restart every time!).
I would, and do, use method 3 because it is the best of the lot. You can use resource files to store the translations in and map the enum values to keys in the resource files. Your database can contain the id of the enum for the status.
1.status names should be easily displayed in different languages,
2.application can search via freetext status names (like googling for "open")
These are interfaces layer's concern, you'd better not mix them in you domain model.
I would setup a mapping between status enum and i18n codes. the mapping could be stored in a file (cached in memory) or hardcoded.
for example: if you use dto or view adatper to render your ui.
public class OrderDetailViewAdapter {
private Order order;
public String getStatus() {
return i18nMapper.to(order.getStatus());//use hardcoded switch case or file impl
}
}
Or you could done this before you populating you dtos.
You could use a similar solution for goal2. When user types text, find corresponding enum from mapping and use enum for search.
Anyway, use db tables the less the better.
Personally, I always use dedicated enum class inside domain. Only responsibility of this class is holding status name (OPEN, CANCELED, SHIPPED, ...). Status name is not visible outside codebase. Also, status could be also stored inside database field as string (varchar or similar).
For the purpose of rendering, depending of number of use cases, sometimes I implement formatting inside formatter (e.g. OrderFormatter::formatStatusName(), OrderFormatter::formatAbbreviatedStatusName(), ...). If formatting is needed often I create dedicated class with all formatting styles needed (OrderStatusFormatter::short(), OrderStatusFormatter::abbriviated()...). Of course, internal mapping is needed to map status name to status title, and this is tricky part. But if you want layering you can't avoid mapping.
Translation is not dealt so far. I translate strings inside templates so formatters are clean of that responsibility. To summarize:
enum inside domain model
formatter inside presentation layer
translation inside template
There is no need to create special table for order status translations. Better choice would be to implement generic translation mechanism, seperated from your business code.

Resources