Fetching NSManagedObjects based on attributes in related objects with inheritance? - core-data

I have a fairly complex CoreData data model with entities inheriting from others and I'm getting an exception when executing a predicate. For example:
#"player.score > 1000";
Where:
Player (abstract)
- name
- tags -> Tag
LocalPlayer : Player
- score
- lives
VirtualPlayer : Player
- difficultyLevel
Tag : NSManagedObject
- name
- color
- player -> Player
I understand why, Tag has a relationship to Player, and score is an attribute on LocalPlayer, so it isn't valid since it isn't on other Player subclasses. But I really don't want lose the hierarchy of my data model.
Is there a way (subqueries, maybe?) to limit my predicate to only run against LocalPlayer objects in the Tag:player->Player releationship? Any suggestions?
Thanks.

If you are attempting to perform a fetch request using this predicate, it is not possible. The predicate is compiled to an SQL statement, and it is validated before sending to the backing database for execution. Interestingly, Core Data implements inheritance in a single, large table. So the SQL statement would actually not fail and return a correct result. But it is failed before execution by the Core Data predicate parser, which validates it against its model. To overcome this, consider promoting the score property to the abstract class Player. Perhaps, store it as an NSNumber, which would allow having a nil value to indicate irrelevance (in cases of VirtualPlayer objects).
You could also reverse your fetch request, fetching all local players with score of 1000, and then taking a list of all the tags:
NSSet* tags = [[moc executeFetchRequest:localPlayersRequest error:NULL] valueForKey:#"#distinctUnionOfSets.tags"];
Note however, that this is less optimal, and you may consider prefetching the tags relationship for quicker union of sets.

You should not have a predicate like this. From a conceptional standpoint, a Player is not guaranteed to have a score. Instead, you should set the entity of your request to LocalPlayer.
Even better, in my opinion, would be to avoid the inheritance complexity altogether. If the attributes list in your question is exhaustive, I would think that you had better simplify the model to just a Player entity to include all the attributes. You could even add a boolean isVirtual to make query filters easier.
Keep it simple and readable. You may "lose the hierarchy" but you will "gain simplicity".

Related

Always valid domain model entails prefixing a bunch of Value Objects. Doesn't that break the ubiquitous language?

The principle of always valid domain model dictates that value object and entities should be self validating to never be in an invalid state.
This requires creating some kind of wrapper, sometimes, for primitive values. However it seem to me that this might break the ubiquitous language.
For instance say I have 2 entities: Hotel and House. Each of those entities has images associated with it which respect the following rules:
Hotels must have at least 5 images and no more than 20
Houses must have at least 1 image and no more than 10
This to me entails the following classes
class House {
HouseImages images;
// ...
}
class Hotel {
HotelImages images;
}
class HouseImages {
final List<Image> images;
HouseImages(this.images) : assert(images.length >= 1),
assert(images.length <= 10);
}
class HotelImages {
final List<Image> images;
HotelImages(this.images) : assert(images.length >= 5),
assert(images.length <= 20);
}
Doesn't that break the ubiquitous languages a bit ? It just feels a bit off to have all those classes that are essentially prefixed (HotelName vs HouseName, HotelImages vs HouseImages, and so on). In other words, my value object folder that once consisted of x, y, z, where x, y and z where also documented in a lexicon document, now has house_x, hotel_x, house_y, hotel_y, house_z, hotel_z and it doesn't look quite as english as it was when it was x, y, z.
Is this common or is there something I misunderstood here maybe ? I do like the assurance it gives though, and it actually caught some bugs too.
There is some reasoning you can apply that usually helps me when deciding to introduce a value object or not. There are two very good blog articles concerning this topic I would like to recommend:
https://enterprisecraftsmanship.com/posts/value-objects-when-to-create-one/
https://enterprisecraftsmanship.com/posts/collections-primitive-obsession/
I would like to address your concrete example based on the heuristics taken from the mentioned article:
Are there more than one primitive values that encapsulate a concept, i.e. things that always belong together?
For instance, a Coordinate value object would contain Latitude and Longitude, it would not make sense to have different places of your application knowing that these need to be instantiated and validated together as a whole. A Money value object with an amount and a currency identifier would be another example. On the other hand I would usually not have a separate value object for the amount field as the Money object would already take care of making sure it is a reasonable value (e.g. positive value).
Is there complexity and logic (like validation) that is worth being hidden behind a value object?
For instance, your HotelImages value object that defines a specific collection type caught my attention. If HotelImages would not be used in different spots and the logic is rather simple as in your sample I would not mind adding such a collection type but rather do the validation inside the Hotel entity. Otherwise you would blow up your application with custom value objects for basically everything.
On the other hand, if there was some concept like an image collection which has its meaning in the business domain and a set of business rules and if that type is used in different places, for instance, having a ImageCollection value object that is used by both Hotel and House it could make sense to have such a value object.
I would apply the same thinking concerning your question for HouseName and HotelName. If these have no special meaning and complexity outside of the Hotel and House entity but are just seen as some simple properties of those entities in my opinion having value objects for these would be an overkill. Having something like BuildingName with a set of rules what this name has to follow or if it even is consisting of several primitive values then it would make sense again to use a value object.
This relates to the third point:
Is there actual behaviour duplication that could be avoided with a value object?
Coming from the last point thinking of actual duplication (not code duplication but behaviour duplication) that can be avoided with extracting things into a custom value object can also make sense. But in this case you always have to be careful not to fall into the trap of incidental duplication, see also [here].1
Does your overall project complexity justify the additional work?
This needs to be answered from your side of course but I think it's good to always consider if the benefits outweigh the costs. If you have a simple CRUD like application that is not expected to change a lot and will not be long lived all the mentioned heuristics also have to be used with the project complexity in mind.

Core Data: storing ordered values in a one-to-many relationship

I'm building a workout app that has an entity called Workout and another one called Exercise.
A workout can contain multiple exercises (thus a one-to-many relationship). I want to show the users of my app the exercises contained in a workout but in an ordered way (it's not the same to start with strength exercises as with the cardio ones).
Apparently, when establishing this kind of relationship in Core Data, I need to use an NSSet, because if I try to use for example an Array where its elements are ordered, I get the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-many relationship: property = "consistsOf"; desired type = NSSet; given type = __NSArray0; value = (
).'
I have tried to check the "ordered" checkmark in my model, but then I get an error saying "Workout.consistsOf must not be ordered".
I have also tried to use an NSDictionary whose keys would be the position and the values would be the exercises themselves, but I'm getting the same error as above.
How can I show the users the exercises that a workout consists of in an ordered way?
Thanks a lot in advance!
P.S.: Here's a screenshot of the properties of my model.
Ordered relationships use NSOrderedSet, but CloudKit doesn't support ordered sets, so you can't use an ordered relationship and CloudKit in the same data model.
To keep an order, you need to have some property on Exercise that would indicate the order. This could be as simple as an integer property called something like index. You'd sort the result based on the index value. If there's something else that also indicates order-- like a date, maybe?-- use that instead of adding a new property.

To Have An ID or Not To Have An ID - Regarding Value Object

Let's say two domain objects: Product and ProductVariety (with data such as color, size etc). The relationship between these two is one-to-many. Conceptually saying in the domain driven design, the ProductVariaty should be a value object, which is not the same object once its data is changed. From the implementation point of view, however, it is better to have some sort identification for the ProductVariaty so that we know which ProductVariety is selected and so on. Is an only solution to convert it to an entity class?
The following is a code segment to illustrate this situation.
#Embeddable
class ProductVariety {...}
#Entity
class Product {
#ElementCollection
private Set<ProductVariety> varities;
...
}
Conceptually saying in the domain driven design, the ProductVariaty should be a value object, which is not the same object once its data is changed
That's not quite the right spelling. In almost all cases (many nines), Value Object should be immutable; its data never changes.
Is an only solution to convert it to an entity class?
"It depends".
There's nothing conceptually wrong with having an identifier be part of the immutable state of the object. For example, PANTONE 5395 C is an Identifier (value type) that is unique to a particular Color (value type).
However, for an identifier like PANTONE 5395 C to have value, it needs to be semantically stable. Changing the mapping of the identifier to the actual color spectrum elements destroys the meaning of previous messages about color. If the identifier is "wrong", then the proper thing to do is deprecate the identifier and nominate a replacement.
Put simply, you can't repaint the house by taking the label off the old paint can and putting it on a new one.
In that case, there's no great advantage to using the identifier vs the entire value object. But its not wrong to do so, either.
On the other hand, if you are really modeling a mapping, and you want to follow changes that happen over time -- that's pretty much the definition of an entity right there.
What it really depends on is "cost to the business". What are the trade offs involved, within the context of the problem you are trying to solve?
Note: if you really do find yourself in circumstances where you are considering something like this, be sure to document your cost benefit analysis, so that the next developer that comes along has a trail of breadcrumbs to work from.

Is every property of an Entity in domain driven design a value object?

I'm reading "Patterns, Principles, and Practices of Domain-Driven Design". The book suggests that properties of an Entity should be value objects in order to model domain's ubiquities language. I've seen many examples like EmailAddress or Age with only one field to model domain concepts. I'm confused about it. Is every property of an Entity a value object? Can you provide examples when we can use normal languages provided data types for properties?
No, not every property of an entity is a value object.
Properties of entities are one of the following:
As you already know, value objects. Value objects represent simple values without identity.
Primitives. These are just value objects from a DDD perspective, really. Primitives are ok to use in DDD, but take care not to become a victim of Primitive Obsession.
Entities. An entity can contain other entities. All entities that have direct (navigable) references between them are part of the same Aggregate. The "top-most" entity within an aggregate is called the Aggregate Root. Only the root has a global identity, inner entities have only local identity.
References to entities of other aggregates. Never reference these directly, use an ID. IDs themselves can in turn be modeled as value objects.
I think that your real question is: Is every value object a Class?
Because you can think that for the Age a Java Integer can be enough and this is true. So you have in your entity Person a value object Age of type Integer, there is no need of an age type.
OOP also says that an object is state + behaviour. In your Age case, I assume that it has no behavior so a simple primitive or wrapper class will do the trick, in fact I would go with option this because is simpler.
My advise is, go with a primitive/wrapper class and if you advert that some behavior is needed in that value object, make a class/type.

Core Data: inverse relationship for two relationships with same type

In my app Core Data model I have Sheet and Text entities. Sheet entity can have two Text's: privacyNotes and termsOfUse.
Both of Text type. So in XCode data modeler I create to-one relationships called "privacyNotes" and "termsOfUse" in Sheet with Text destination. Next goes to-one relationship "sheet" in Text. Then I select that Text.sheet relationship as inverse for Sheet.privacyNotes. So far so good. But when I set same Text.sheet relationship as inverse for Sheet.termOfUse XCode deletes this relationship as inverse Sheet.privacyNotes!
I understand that relationships in DB can be not so simple compared to Objective-C objects relationships, but I really don't get why SQLite or (CoreData) can't reuse one relationship as inverse for FEW other relationships?
A little peek under the abstraction hood might be enlightening*: a relation can only be the inverse for exactly one other relation because, in the backing store, they're represented by the same data. If a Text and a Sheet can have a certain relationship, Core Data does what a good human data modeler would do and stores that relationship as succinctly as possible. The relation properties of the entity objects are just ways of looking at that relationship.
To get the effect of what you're going for: go ahead and give Sheet properties for privacyNote and termsOfUse; but give Text properties like sheetIAmTermsFor and sheetIAmPrivacyNoteFor, and set them as inverses appropriately. Then in the Text class, add a synthetic property along these lines:
// in interface
#property (nonatomic, readonly) Sheet *sheet;
// in impl
-(Sheet *)sheet
{
if ([self sheetIAmTermsFor])
return [self sheetIAmTermsFor];
else
return [self sheetIAmPrivacyNoteFor];
}
If you want to write a setter too, you'll have to decide which role that setter should bestow on the Text (which Core Data can't figure out for you, another reason a property can't be the inverse of two different properties.)
If you need to enforce a constraint that a Text can only ever be a "privacyNote" or a "terms" but never both, override the setters for sheetIAmTermsFor and sheetIAmPrivacyNoteFor, following Apple's pattern in the docs, and have each null the other property when set.
(* Apple regards the SQLite databases Core Data generates as private to their implementation, but inspecting their schemas can be very educational. Just don't be tempted to write shipping code that goes behind CD's back to poke at the db directly.)
You are far better off having a one to many relationship between Sheet and Text with a validation limit of 2. Then you should have a type property in the text which declares it as either a privacyNotes or termsOfUse. From there you can add convenience methods to your Sheet subclass that allows you to retrieve either one.

Resources