How can you go about modelling an object that can have multiple simultaneous states?
For example, you could have a person that's waiting for a bus. That's one state. But they could also be reading a newspaper while waiting for the bus. Furthermore, they could be thinking about something while reading the newspaper. They could also be sniffing their nose because they have a cold. That's a four states in all taking place at the same time.
States are often just steps in a process. Instead of representing them as values, represent the process itself as a class, and have it make decisions on what should be done, not just tell what state it is in.
In your case you would have multiple simultaneous processes going on, so your Person class would have an instance of DailyCommuteProcess, BodyClockProcess, and so on each of which will take care of corresponding functions.
This approach (encapsulating the state and the logic that acts on a state in a separate class, as opposed to making the consuming class look at the public properties and act), is known as the Tell, Don't Ask principle.
Use an array to store all states the object is currently in.
MrBean.states = {
"WaitingForBus",
"ReadingNewspaper",
"Sniffling",
"ThinkingAboutPaintings"
};
How you model the states is entirely upto you. The above is a simple example where states are modeled by strings, and carry zero extra information.
When the concepts are orthogonal (independent) then they can be simply modelled as independent values, e.g.
class Person
{
Location location; // bus stop, home etc...
Motion motion; // sitting, walking, running
Topic thinkingAbout;
boolean sniffing;
boolean blinking;
boolean breakingWind;
}
It's reasonable that a person can do all of these at once, so there are no constraints. That is, they can be sitting/walking/running at a given location (bus stop, home, work), they can at the same time be thinking about some topic, and could also be sniffing, blinking and doing other things at the same time.
Each substate itself is exclusive - a person can only be at one place, have one kind of motion, thinking about one thing.
When there are constraints, the same model can be used, but in conjunction with a validation framework to ensure the state is valid.
For example, if we added 'boolean sneezing'. When sneezing is true, then blinking should be true also, since it's not possible to keep your eyes open while sneezing. The validation model would encode this constraint.
Thinking in terms of states, the independent values can be modelled collectively as a single state by taking the cartesian product of each substate.
Related
I was studying about the markov property in reinforcement learning, which is supposed to be one of the important assumptions of this field. In that it says, that while considering the probability of the future, we consider only the present state and actions and not that of the past. An important corollary that arises when we consider the probability of the present state given future state/action, the future state/action can't be ignored as it has valuable information in the computation of the present probability.
I do not understand this second statement. From the point of view of the future event, the present event seems to be the past for this future event. Then why are we considering this past event?
Let's focus on these two sentences individually. The Markov Property (which should apply in your problem, but in reality doesn't have to) says that the current state is all you need to look at to make your decision (e.g. a "screenshot" -aka observation- of the chess board is all you need to look at to make an optimal action). On the other hand, if you need to look at some old state (or observation) to understang something that is not implied in your current state, then the Markov property is not satisfied (e.g. you can't usually use a single frame of a videogame as a state, since you may be missing info regarding the velocity and acceleration of some moving objects. This is also why people use frame-stacking to "solve" video games using RL).
Now, regarding the future events which seems to be considered as past events: when the agent takes an action, it moves from one state to another. Remember that in RL you want to maximize the cumulative reward, that is the sum of all the rewards long-term. This also mean that you basically want to take action even sacrifying instantaneous "good" reward if this means obtaining better "future" (long-term) reward (e.g. sometimes you don't want to take the enemy queen if this allows the enemy to check-mate you in the next move). This is why in RL we try to estimate value-functions (state and/or action). State value-functions is a value assigned to a state which should represent how good is being in that state in a long-term perspective.
How is an agent supposed to know the future reward (aka calculate these value functions)? By exploring a lot of states and taking random actions (literally trial and error). Therefore, when an agent is in a certain "state1" and has to choose between taking action A and action B, he will NOT choose the one that has given him the best instantaneous reward, but the one which has made him get better rewards "long-term", that is the action with the bigger action-value, which will take into account not only the instantaneous rewards he gets from the transition from state1 to the next state, but also the value-function of that next state!
Therefore, future events in that sentence may seem to be considered as past events because estimating the value function require that you have been in those "future states" a lot of times during past iterations!
Hope I've been helpful
Let's say you are trying to model a rent-with-driver service. You have a Driver class to represent the informations about the drivers. Each driver can have one or more (1..*) cars available, represented in a Car class (I guess). Each driver can also insert one or more (1..*) shifts during which they are available (shifts are characterized by an area, a day a starting_time and an ending_time), and for this reason I imagined a Shift class was needed. A User class can search() for available cars in a given area at a given time.
You have to draw the class diagram of this system and the sequence diagram of the search() use case.
How do you represent the Shift class in such scenario?
I mean, all those classes clearly don't represent a single entity, but rather a set of data of that type. Now, for a user or a driver you can easily represent them by taking a single user or a single driver object and they will summarize the behaviour of all of them pretty good. But how do you do this with the Shift class? The search() operation must be done on the whole dataset rather than on a single object and a single object clearly doesn't hold all the information about all the shifts. Plus, surely a shift object doesn't have a getAvailableCars() method, since, again, it hold the informations about a single shift.
It goes without saying that something like this (which is what I come up with until now about the sequence diagram)
looks too simplistic to be correct.
Should I introduce a ShiftsList class and a CarsList class? Should I just treat it as a set of objects in some other way?
I can imagine how I would model a problem like this with an E/R diagram since in that case the Shift entity would be a database's table, which is a set of data by design, but I'm having a really hard time trying to put this in UML.
Your design sounds reasonable, except I would replace User with Driver.
Do not model lists in UML. A list is a way to realize an association with 0..* multiplicity.
The available cars is just a returned Car class with multiplicity 0..*.
In the class diagram you get a * cardinality relationship, which you could qualify as a composition. It allows the class holding the "search" function (the front-end) to reference all the Shift instances.
You should think first of the signature of the front-end, that carries the data for your system, i.e. a set of Driver, a set of Car...
It carries the high level operations :
i.e.
void addAvailability(driverName:String, carLicense:String, area:String, ....)
and also what are the parameters to the search function (? not given in question)
Probably, you want a class "Availability" to store the data the drivers are making available.
It carries a reference to a Driver (1 cardinality) and the area/date pair.
Or perhaps a list of corresponding Shift instances.
Then depending on the parameters of the search, you want Availability to have a "matches(searchCriterion):bool".
To model an iteration in a sequence diagram you do have the "loop" fragment, but is it really useful ?
Do the sequence diagram of when the "matches" function returns true, you'll be fine.
Put one lifeline for the User (an actor) that invokes operations on the front-end class (search). The front-end invokes matches() on a shift, if it's true queries to find the associated driver, then his car(s). Draw the returned value ({"Joe's mercedes"}) on the return arc to the actor.
Just a quick answer. I think you touched only the very tip of an iceberg. If you want to plan the shifts, the whole thing gets a bit more complex than one Shift class.
You don't want to model user as part of the domain (he is not). User is modelled as an actor in Use Case model. He interacts with the system, not with the real life objects (things).
Think about drivers inserting shifts, this model rarely works. Usually you need kind of a manager to synchronize and plan things (Shift Plan class, which contains [0..*] Shifts), so that you don't have extremes (a day without any driver or a day when all drivers are on shift, but not enough customers etc.)
The relationship between the driver and the car is this: "The driver drives the car". That's the most important information. There is no real relation between a driver and all available cars in real life, until he drives one. We model real life relations in order to deliver the real life value.
You probably want to model areas as an enumeration to keep things consistent.
Hope this helps to get you started in the right direction. Keep us informed of your progress ;)
Regards, Trueanalysis
I apologize for so many questions, but I felt that they make the most sense only when treated as a unit
Note - all quotes are from DDD: Tackling Complexity in the Heart of Software ( pages 250 and 251 )
1)
Operations can be broadly divided into two categories, commands and
queries.
...
Operations that return results without producing side effects are
called functions. A function can be called multiple times and return
the same value each time.
...
Obviously, you can't avoid commands in most software systems, but the
problem can be mitigated in two ways. First, you can keep the commands
and queries strictly segregated in different operations. Ensure that
the methods that cause changes do not return domain data and are kept
as simple as possible. Perform all queries and calculations in methods
that cause no observable side effects
a) Author implies that a query is a function since it doesn't produce side effects. He also notes that function will always return same value, by which I assume he means that for the same input we will always get the same output?
b) Assume we have a method QandC(int entityId) which queries for specific domain entity, from which it extracts certain values, which in turn are used to initialize a new Value Object and this VO is then returned to the caller. Isn't according to above quote QandC a function, since it doesn't change any state?
c) But author also argues that for same input a function will always produce same output, which isn't the case with QandC, since if we place several calls to QandC, it will produce different results, assuming that in the time between the two calls this entity was modified or even deleted. As such, how can we claim QandC is a function?
d)
Ensure that the methods that cause changes do not return domain data
...
Reason being that the state of returned non-VO may be changed in some future operations and as such the side effects of such methods are unpredictable?
e)
Ensure that the methods that cause changes do not return domain data
...
Is a query method that returns an entity still considered a function, even if it doesn't change any state?
2)
VALUE OBJECTS are immutable, which implies that, apart from
initializers called only during creation, all their operations are
functions.
...
An operation that mixes logic or calculations with state change
should be refactored into two separate operations. But by definition,
this segregation of side effects into simple command methods only
applies to ENTITIES. After completing the refactoring to separate
modification from querying, consider a second refactoring to move the
responsibility for the complex calculations into a VALUE OBJECT. The
side effect often can be completely eliminated by deriving a VALUE
OBJECT instead of changing existing state, or by moving the entire
responsibility into a VALUE OBJECT.
a)
VALUE OBJECTS are immutable, which implies that, apart from
initializers called only during creation, all their operations are
functions ... But by definition, this segregation of side effects into
simple command methods only applies to ENTITIES.
I think author is saying all methods defined on VOs are functions, which doesn't make sense, since even though a method defined on a VO can't change its own state, it still can change the state of other, non-VO objects?!
b) Assuming method defined on an entity doesn't change any state, do we consider such a method as being a function, even though it is defined on an entity?
c)
... consider a second refactoring to move the responsibility for the
complex calculations into a VALUE OBJECT.
Why is author suggesting we should only refactor from entities those function that perform complex calculations? Why instead shouldn't we also refactor simpler functions?
d)
... consider a second refactoring to move the responsibility for the
complex calculations into a VALUE OBJECT.
In any case, why is author suggesting we should refactor functions out of entities and place them inside VOs? Just because it makes it more apparent to the client that this operation MAY be a function?
e)
The side effect often can be completely eliminated by deriving a VALUE
OBJECT instead of changing existing state, or by moving the entire
responsibility into a VALUE OBJECT.
This doesn't make sense, since it appears author is arguing if we move a command ( ie operation which changes the state ) into a VO, then we will in essence eliminate any side-effects, even if command is changing the state. So any ideas, what was author actually trying to say?
UPDATE:
1b)
It depends on the perspective. A database query does not change state
and thus has no side effects, however it isn't deterministic by
nature, since as you point out the data can change. In the book, the
author is referring to functions associated with value object and
entities, which don't themselves make external calls. Therefore, the
rules don't apply to QandC.
So author was describing only functions that don't make external calls and as such QandC isn't a type of function that author was describing?
1c)
QandC does not itself change state - there are no side effects. The
underlying state may be changed out of band however. Due to this, it
is not a pure function.
But it also isn't the Side-Effect-Free function in the sense author defined them?
1d)
Again, this is based on CQS.
I know I'm repeating myself, but I assume discussion in the book is based on CQS and CQS doesn't consider QandC as Side Effect Free function due to a chance of entity returned by QandC having its state modified ( by some other operation ) sometime in the future?
1e)
It is considered a query from the CQRS perspective, but it cannot be
called a function in the sense that a pure function on a VO is a
function due to lack of determinism.
I don't quite understand what you were trying to say ( the confusing part is in bold ). Perhaps that while QandC is considered a query, it is not considered a function due to returning an entity and such the side-effects are unpredictable, which makes QandC a non-deterministic by nature
So author is only making those statements ( see quote in 1e ) under the implicit assumption that no operation defined in VO will ever try to change the state of non-VO objects?
2d)
Given that VOs are immutable, they are a fitting place to house pure
functions. This is another step towards freeing domain knowledge from
technical constraints.
I don't understand why moving function from entity to VO would help free domain knowledge from technical constraints ( I'm also not really sure what you mean by technical – technical as in technology-related or... )?
I assume other reason for putting function in VO is because it is that much more obvious ( to client ) that this is a function?
2e)
I view this as a hint towards event-sourcing. Instead of changing
existing state, you add a new event which represents the change. There
is still a net side effect, however existing state remains stable.
I must confess I know nothing about even-source programming, since I'd like to first wrap my head around DDD. Anyway, so author didn't imply that just moving a command to VO would automatically eliminate side-effects, but instead some additional actions would have to be taken ( such as implementing event-sourcing ), only he "forgot" to mention that part?
SECOND UPDATE:
2d)
One of the defining characteristics of an entity is its identity ....
By placing business logic into VOs you can consider it outside of the
context of an entity's identity. This makes it easier to test this
logic, among other things.
I somehwat understand the point you're making ( when thinking about the concept from distance ), but on the other hand I really don't. Why would function within an entity be influenced by an identity of this entity ( assuming this function is pure function, in other word it doesn't change state and is deterministic )?
2e)
Yes that is my understanding of it - there is still a net "side
effect". However, there are different ways to attain a side effect.
One way is to mutate existing state. Another way is to make the state
change explicit with an object representing that change.
I - Just to be sure ... From your answer I gather that author didn't imply that side-effects would be eliminated simply by moving a command into VO?
II - Ok,if I understand you correctly, we can move a command into VOs ( even though VOs shouldn't change the state of anything and as such shouldn't cause any side-effects ) and this command inside VO is still allowed to produce some sort of side effects, but this side effect is somehow more acceptable ( OR MORE CONTROLLABLE ) by making state change explicit ( which I interpret as the thing that changed is returned to the caller as VO )?
3) I must say that I still don't quite understand why state-changing method SC shouldn't return domain objects. Perhaps because non-VO may be changed in some future operations and as such the side effects of SC are very unpredictable?
THIRD UPDATE:
Delegating the management of state to the entity and the
implementation of behavior to VOs creates certain advantages. One is
basic partitioning of responsibilities.
a) You're saying that even though a method describes a behavior of an entity ( and thus entity containing this method adheres to SRP ) and as such belongs in the entity, it may still be a good idea to move it into VO? Thus in essence, we would partition a responsibility of an entity into two even smaller responsibilities?
b) But won't moving behavior into VO basically turn this entity into a mere data container ( I understand that entity will still manage its state, but still ... )?
thank you
1a) Yes. The discourse on separating queries from commands is based on the Command-query separation principle.
1b) It depends on the perspective. A database query does not change state and thus has no side effects, however it isn't deterministic by nature, since as you point out the data can change. In the book, the author is referring to functions associated with value object and entities, which don't themselves make external calls. Therefore, the rules don't apply to QandC. Determinism could be fabricated however, offering degrees of "pureness". For instance, a serializable transaction could be created which can ensure that data doesn't change for its duration.
1c) QandC does not itself change state - there are no side effects. The underlying state may be changed out of band however. Due to this, it is not a pure function. However, the restriction that QandC doesn't change state is still valuable. The value is fittingly demonstrated by CQRS which is the application of CQS in distributed scenarios.
1d) Again, this is based on CQS. Another take on this is the Tell-Don't-Ask principle. Given an understanding of these principles however, the rule can be bent IMO. A side-effecting method could return a VO representing the result for instance. However, in certain scenarios such as CQRS + Event Sourcing it could be desirable for commands to return void.
1e) It is considered a query from the CQRS perspective, but it cannot be called a function in the sense that a pure function on a VO is a function due to lack of determinism.
2a) No, a VO function shouldn't change state of anything, it should instead return a new object.
2b) Yes.
2c) Because functional purity tends to become more important in more complex scenarios. However, as you point out, isn't a clear and definitive rule. It shouldn't be based on complexity as much as it is based on the domain at hand.
2d) Given that VOs are immutable, they are a fitting place to house pure functions. This is another step towards freeing domain knowledge from technical constraints.
2e) I view this as a hint towards event-sourcing. Instead of changing existing state, you add a new event which represents the change. There is still a net side effect, however existing state remains stable.
UPDATE
1b) Yes.
1c) It is a side-effect free function, however it is not a deterministic function because it cannot be thought to always return the same value given the same input. For example, the function that returns the current time is a side-effect free function, but it certainly does not return the same value in subsequent calls.
1d) QandC can be thought of as side-effect free, but not pure. Another way to look at functional purity is as referential transparency - the ability to replace a function call by its value without changing program behavior. In other words, asking the question does not change the answer. QandC can guarantee that, but only within a context such as a transaction. So QandC can be thought of as a function, but only in a specific context.
1e) I think the confusing part is that the author is talking specifically about functions on VOs and entities - not database queries, where as we are talking about both. My statement extends the discussion to database queries and CQRS given certain restrictions, ie an ambient transaction.
2d) I can see how what I said was a bit vague, I was getting lazy. One of the defining characteristics of an entity is its identity. It maintains its identity throughout its life-cycle while its state may change. By placing business logic into VOs you can consider it outside of the context of an entity's identity. This makes it easier to test this logic, among other things.
2e) Yes that is my understanding of it - there is still a net "side effect". However, there are different ways to attain a side effect. One way is to mutate existing state. Another way is to make the state change explicit with an object representing that change.
UPDATE 2
2d) This particular point can be argued or can be a matter of preference. One perspective is the idea is based on the single-responsibility principle (SRP). The responsibility of an entity is the association of an identity with behavior and state. Behavior combines input with existing state to produce state transitions. Delegating the management of state to the entity and the implementation of behavior to VOs creates certain advantages. One is basic partitioning of responsibilities. Another is more subtle and perhaps more arguable. It is the idea that logic can be considered in a stateless manner. This allows thinking about such logic easier and more like thinking about a mathematical equation where all changes are explicit - no hidden state.
2e.1) Yes, eliminating a net side effect would alter behavior, which is not the goal.
2e.2) Yes.
3) Commands returning void have several advantages. One is that they become naturally more adept in async scenarios - no need to wait for a result. Another is that it allows you to represent the operation as a single command object - again, because there is no return value. This applies in CQRS and also event sourcing. In these cases, any command output is dispatched as an event instead of a result. But again, if these requirements don't apply returning a result object can be appropriate.
UPDATE 3
a) Yes, and this is a specific type of partitioning.
b) The responsibility of the entity is to coordinate behavior by delegating to VOs and applying the resulting state changes.
I have a GUI tool that manages state sequences. One component is a class that contains a set of states, your typical DFA state machine. For now, I'll call this a StateSet (I have a more specific name in mind for the actual class that makes sense, but this name I think will suffice for the purpose of this question.)
However, I have another class that has a collection (possibly partially unordered) of those state sets, and lists them in a particular order. and I'm trying to come up with a good name for it - not just for internal code, but for customers to refer to it.
The role of this particular second collection is to encapsulate the entire currently used/available collection of StateSets that the user has created. All of the StateSets will be used eventually in the application. A good analogy would be a hand of cards versus the entire table: The 'table' contains all of the currently available hands, while the 'hand' contains a particular collection of cards.
I've got these as starter ideas I could throw out for the class name; I'm not comfortable with either at the moment:
Sequence (maybe...with something else tacked on to the name)
StateSetSet (reasonable for code, but not for customers)
And as ewernli mentions, these are really technical terms, which don't really convey a the idea well. Any other suggestions or ideas?
Sequence - Definitely NOT. It's too generic, and doesn't have any real semantic meaning.
StateSetSet - While more semantically correct, this is confusing. You have a sequence, which implies order, which is different from a set, which does not.
That being said, the best option, IMO, is StateSetSequence, as it implies you have a sequence of StateSet instances.
What is the role/function of you StateSetSet?
StateSetSet or Sequence are technical terms.
Prefer a term that convey the role/function of the class.
That could well be something like History, Timeline, WorldSnapshot,...
EDIT
According to your updated description, StateSet looks to me like StateSpace (the space of all possible states). If the user can then interactively create something, it might be appropriate to speak of a Workspace. If the user creates various state spaces of interest, I would then go for StateSpaceWorkspace. Isn't that a cool name :)
"StateSets" may be sufficient.
Others:
StateSetList
StateSetLister
StateSetListing
StateSetSequencer
I like StateSetArrangement, implying an ordering without implying anything about the underlying storage mechanisms.
Something keeps showing up in my programming, and it is that two things are the same from some viewpoint, but different from another. Like, imagine you build a graph of rail stations, connected by trains, then the classes Vertex and RailStation are sometimes the same, other times not.
So, imagine I have a graph that very much represents rail stations and trains. Then I hand this graph to another object, which deletes some vertices, and then I want the corresponding rail stations to be gone.
I don't want to make rail stations "properties" of vertices, they're not. Also, the problem is symmetrical: If I erase a railstation, I want the corresponding vertex to be gone. What is the proper OO way to model or correspondences. I'm willing to go a few extra miles by writing some support methods or classes, if in the end the overall usage is simple and easy.
I'm currently using the Smalltalk programming language, but the question isn't really smalltalk-specific, I think. I just mention it because in Smalltalk, you can do cool tricks like examining the call stack, which might be helpful in this context.
Update:
Well, RailStations aren't Vertices! Are they?
Ok, let us consider real code, as demanded in the answers. Let me model a person with children. That's the easiest thing, right? Children should also know their parents, so we have like a doubly linked tree. To make disbanding parents from children easier, I model the link between parent and child as a Relationship, with properties parent and child.
So, I could implement parent>>removeChild: perhaps like this
removeChild: aChild
(parent relationshipWith: aChild) disband.
So, a parent has a collection of relationships, not of children. But each relationship corresponds to a child. Now I want to do things like this:
parent children removeAllSuchThat: [:e | e age < 12]
which should remove the relationship and the child.
Here, relationships and children correspond in some sense. So, what do I do now? Don't get me wrong, I'm fully aware that I could solve the problem without introducing Relationship classes. But indeed, parents and children actually do share a relationship, so why not model that and use it to help disbanding double links less imperatively?
In your problem domain, aren't stations a kind of vertex? In which case, why not derive Station from Vertex?
Notice the use of the phrase "in your problem domain". Your problem appears to be about the use as railway stations appearing in a graph. So yes, in that domain, stations are vertexes. If it was a different problem domain, say a database on railway station architecture, they may well not be. Most modern languages support some idea of namespaces to allow you to have different kinds of entity with the same names in different domains.
Regarding your parent/child problem, once again you are being too general. If I were modelling mathematical expressions and sub expressions, if I remove a parent I would want to remove and delete/free all subexpressions. OTOH, ff I were modelling legal responsibility relationships in the UK population, then when a responsibility isis dissolved (say because of a divorce), I only want to remove the relationship, and NOT delete/free the child, which has its own independent existence.
It seems like you just want RailStation to inherit from Vertex (is-a relationship). See this smalltalk tutorial on inheritance. That way, if you have a graph of RailStations, an object used to dealing (generically) with graphs of Vertexes would handle things right naturally.
If this approach won't work, be more specific (preferably with real code).
From your description of the problem, you have a one-to-one correspondence of stations to vertices and deleting a station should automatically delete the corresponding vertex (and vice-versa). You also mentioned building "a graph of rail stations, connected by trains", by which you apparently mean a graph in which stations are vertices and trains are edges.
So, in what way is a station not a vertex? If the station does not exist except as a vertex, and if a vertex does not exist except as a station, then what benefit do you see in maintaining them as two distinct-but-linked entities?
As I understand your situation, station-isa-vertex and inheritance is the way to model that.
Having a Relationship object is a good idea.
I think the appropriate question here is "which use should be made of it?".
Probably Parent and Child classes are extending the same Person superclass, so they'll have some attributes in common, age for example.
In my idea, I can see the following: Parent and Child objects have to know each other, so both classes have to keep a link to the same Relationship.
The Relationship object keeps a one-to-many relation between a single parent and a certain number of children, and it'll keep a reference to each Person object.
This way you can implement the whole disbanding logic within the Relationshp object, more or less sophisticated as you wish. You can query the Relationship object to know which members of the family match your requirements to do something. You can make the relationship to disband (and destroy) safely, as it will know all members and would ask them to break the reference and then it would be ready to destroy, or ask to some member to leave the family, keeping the Relationship object alive.
But that's not all. Relationship should be really a superclass, extended by HierarchicalRelationship and PeerRelationship (or FriendRelationship).
This specialization lets you have Parent(s) and Child(ren) to link between other hierarchies in a completely traversal way.
The true concept behind this is that your Relationship objects are the key to query and organize the whole bunch of Person objects (or Vertex objects) in a scalable and structured way, so the whole data domain you end up with is usable in any sense you like, whether you want to disband groups or walk a certain path (or railroad) between them.
Sorry for the huge amount of metaphores.
Take a look at Fame, see http://www.squeaksource.com/Fame.html
We use a specialized subclass of Collection that updates the opposite end when you add or remove elements. Also, you can annotate your classes with pragmas to annotate relations. These pragmas are used by the Fame framework to do all kind of nice stuff.