Skip to content

Design Goals

Anthony Sneed edited this page Jan 1, 2016 · 7 revisions

Trackable Entities Express Design Goals

Here is a list of design goals for building Trackable Entities Express:

1. More focus on libraries than on tools [Issue #1]

  • We should allow developers to use a set of libraries to quickly get up and running without relying as much on using special tools, such as T4 templates or Visual Studio extensions.
  • This will allow developers to use any kind of code editor or IDE on any platform, rather than requiring Visual Studio and Windows.
  • While the TE main tooling provided a great deal of productivity, it also resulted in less flexibility.

2. Better support for plain old entities [Issue #2]

  • While server-side entities were pretty clean, only requiring an ITrackable interface, things were more complicated on the client, where collections needed to be of type ChangeTrackingCollection<TEntity> in order for graph change-tracking to take place correctly.
  • The easiest path for TE was to use EF with a database-first approach, reverse-engineering entities from an existing database schema using the EF design wizard or EF Power Tools, so that T4 templates could generate entities with embedded change trackers.
  • For TE Express, we should provide first-class support for a code-first approach with plain old entities that only need to implement ITrackable. This will allow developers to write some entities from scratch and use EF Code First on the server-side with migrations. Reverse engineering entities from an existing database would also be supported, but the entities would be simpler and much easier to share between client and server.

3. Better separation of concerns for client-side change tracking [Issue #3]

  • If there's one thing that bothers me with the implementation of client-side change tracking in TE main is that entities use ChangeTrackingCollection<TEntity> as the collection type for enumerable properties (for example, Order.OrderDetails is of type ChangeTrackingCollection<OrderDetail>). While using an extension of ObservableCollection was handy because it supported two-way data binding for rich clients, a more ideal solution would be separate change tracking entirely from entities, so that developers would be free to use any collection type they wish.
  • With TE Express I would like to create a ChangeTracker class who's sole responsibility would be to track changes on graphs of objects. Entities are registered for change tracking when they are attached to the change tracker.

4. More modular and friendly to dependency injection [Issue #4]

  • I'd like to aim for factoring classes so that there are cleaner division of responsibilities. For example, there should be an IGraphTraverser (or something like that) which is only responsible for graph traversal and accepts an IObjectVisitor whose responsibility is to prevent recursion when the traverser encounters cycles.
  • We should use interfaces as much as possible (within reason) in order to support better testability with mocking or dependency injection.

5. Continued use of ITrackable for tracking change state on entities [Issue #5]

  • In contrast with frameworks such as OData or Breeze, TE should continue to attach change state to individual entities rather than sending them as a blob to a backend service.
  • Sending change state as a data structure which is separate from entities requires services to be intimately concerned with change tracking. For example, Breeze sends change state in the request body as a separate structure, together with the message body, and the service must know how to unpack it.
  • Most important, per-entity change state makes it much easier to debug entity persistence issues. You can see plainly what the change state of an entity is as it travels across the wire, and method parameters on controller actions (for Web API) do not need to be altered to accommodate an extra payload of change state.
  • We only care about change state in a service when it comes time to persist changes, which we can do with Entity Framework, for example, by calling the ApplyChanges extension method for DbContext supplied by Trackable Entities via a server-side NuGet package.

6. Client-side model metadata [Issue #6]

  • The ChangeTracker should accept for model metadata to indicate things like relationship-types for navigation properties, for example, 1-1, 1-M, M-1, M-M, which would affect the way in which entities are change tracked.
  • The metadata could be supplied programmatically via some sort of fluent API.

7. Convention-based metadata [Issue #7]

  • By default metadata could be built based on a set of conventions.
  • Developers can override conventions-based metadata by specifying metadata manually.

8. Snapshot-based change tracking [Issue #8]

  • TE main does only real-time change tracking. The limitation this imposes is that entities need to be observable via INotifyPropertyChanged and a PropertyChanged event that is fired by each property setter in an entity. This is also why an observable collection is used for change-tracking, so that inserts and deletes can immediately update entity change state.
  • Other frameworks, such as Entity Framework, have supported POCO entities by taking a snapshot of entity property values and relationships with other entities. Change state is only updated when a specific event takes place, such as calling DetectChanges or SaveChanges. While EF6 and before support real-time change tracking, it required the use of dynamic runtime proxies, which will most likely not be supported in EF7 or later.
  • When one or more entities is added to a ChangeTracker and Tracking is enabled, we should build a graph of ChangeDetector's which mirror entity relations in the graph. This will allow us to traverse the graph of change detectors in order to paint change state on entities.
  • Each ChangeDetector should capture a snapshot of property names and values on an entity, and it should also maintain a reference to the entity for which it is tracking change state.
  • When DetectChanges is called on a ChangeTracker, it can traverse a graph of ChangeDetector's, comparing current with original property values and relationships. The advantage of retaining a snapshot of an entity's properties and relationships is that a RevertChanges method could revert an entity to its original state.
  • Another advantage of using snapshots is that change-tracking, as opposed to data-binding, is usually only needed before sending changes to a service for persistence.
  • We should try to avoid serialization, except when absolutely necessary, which should provide better performance and decouple us from serialization concerns, such as cyclical reference handling.
  • A GetChanges method would implicitly invoke DetectChanges but return just entities which have been added, modified or deleted. In the past this is where we have serialized object graphs, but this was the easy (lazy) way to create a deep copy of an object graph. We should be able to accomplish the same thing without resorting to serialization. We can continue to use a visitor pattern to stop recursion when encountering cycles.

9. Observer-based change tracking [Issue #9]

  • While allowing for on-demand change-tracking, we should also plan to support real-time change-tracking with observable entities and collections. This means simply checking for the implementation of certain interfaces (such as INotifyPropertyChanged) and providing real-time change tracking by subscribing to events.

10. Support for immutable types [Issue #10]

  • We can support immutable types in order to improve performance when detecting changes either on-demand or real-time. The reason for better performance is that a reference check can be made to see if a new instance has been created as the result of a property change on an entity. If a reference equality check succeeds, we know that no properties have changed on an entity and graph traversal can skip the node and all its related branches, thereby increasing the speed of graph traversal.
  • There will need to be a way for a developer to indicate to the ChangeTracker that a type is immutable, and developers would be responsible for providing immutable entities.