Flex Data Management Services manages data for one or more types of objects each called a destination. The name destination comes from the fact that Data Management Services is built on the general messaging feature found in Data Management Services, where the term is used for targets for a message send/receive operation. A destination corresponds to a server-side data adapter that receives messages from the client. The adapter processes the message and sends an acknowledgement message to the client. It may additionally push out those messages to other clients, which are subscribed to receive those messages.
A destination may manage data for more than one class. In fact the classes of data that it manages are transparent to Data Management Services, as you never need to declare explicitly the class of data a specific destination uses. The client simply is coded to expect objects returned by the server adapter for a particular destination and Data Management Services handles sending and receiving them.
If you are using strongly typed ActionScript objects, you must mark them with the [Managed] metadata attribute for them to be used with Data Management Services. This attribute adds data binding support for all properties and is an extension of the [Bindable] tag, which you can use to add data binding to your ActionScript classes. For any property whose value is itself a [Managed] type, Data Management Services will listen to changes on properties of this managed property's value as well, so it can manage an entire tree of data underneath the top-level object.
If you are using anonymous objects, those without an explicitly declared type, Data Management Services wraps these objects with a wrapper of type mx.data.ManagedObjectProxy. For Data Management Services to detect changes you make to your object, you must make them using this wrapper instance. For the createItem and getItem methods, make sure to get the wrapper version of the object that is returned to you and use that in your application.
A client uses three calls to get a managed object:
createItem takes a version of the object with or without identity properties assigned. It returns an AsyncToken you use in the event handler to retrieve the created object.getItem takes an Object with the identity properties to retrieve and returns an AsyncToken.fill enables you to define your own variants, each of which takes a unique set of parameters. Each invocation of a fill method is tagged with the specific parameter values you pass. It is assumed that each fill method with the same set of parameters will return a consistent set of results. Typically each fill method turns into a query or method call on the adapter side, which returns a list of objects that are managed by this destination.After you have a managed object, Data Management Services listens for change events on the properties of each object using the data binding support mentioned above. When the first property on an object is modified in a given transaction, a copy of the object is made. This is the original version of the object used for conflict detection. An update message containing the original version, the new version including changes made, and the list of changed property names is put into a queue. If you call the commit function, this queue of changes is sent to the server.
Other calls that will queue updates include the createItem and deleteItem methods. You also listen for any changes made to an ArrayCollection property. The ArrayCollection class corresponds to a java.util.List in Java and has similar functionality. If new items are added to an ArrayCollection class managed in Data Management Services, it implicitly creates the items and adds them to the ArrayCollection class at the same time. After a commit, this batch of changes is sent to the server side adapter.
The server-side adapter processes the list of changes in this batch and decides for each change whether to commit it, fail it due to an error, or fail it due to a conflict. Adapters can either work in a transactional mode or in a nontransactional mode. When transactions are enabled, all of the changes are either committed or rolled back. When transactions are not used, an adapter may choose to commit some changes, while others might fail because of an error in the data or generate conflicts with changes made by other users. If a server rolls back a change, the client's state is rolled back automatically. The client also receives details about any errors encountered on the server, allowing the user to make corrections.
For each modified or newly created item, Data Management Services also may optionally go through and re-execute each fill method currently cached by at least one client. It re-executes the fill method to determine whether that item's membership in that list of items has changed. When it pushes the update messages to other clients, it includes the item's position and membership info in each fill method cached on that client so the item can be automatically added to or removed from any list as necessary, based on its new state. For example, a sales manager's client might store the list that represents prospective accounts greater than a certain amount. As a salesperson updates his or her prospective account info, the query would be run again, updating the manager's screen automatically.
Each client has an autoMerge property that controls how changes made by other clients are merged into this client's current view of the data. If autoMerge is false, messages are queued up until the application invokes the merge() method explicitly. If autoMerge is true, they are applied as they come in. If the client has pending, uncommitted changes made to an item that conflict with the merged changes, a conflict is generated and added to the list of unresolved conflicts for this client.
Conflicts can either be detected on the server because an attempt was made to update an object with stale data, or on the client because a change was received for data that the client is in the midst of modifying. In either case, this model of conflict detection brings the error to the user as soon as possible, which improves the efficiency of users using the application. A list of conflicts is maintained on the client and simple APIs are used to allow a programmer to resolve the conflict.
In this API, the Conflict object contains the original version of the item (before this client's changes were made), the current client's version of the item, and the current server version of the item. There are methods to accept either the client's current version of the data or the server's current version of the data. Whichever you choose as a starting point, your code can then add changes from the other one to create a merged copy perhaps based on feedback from the user.
Bindable properties make it easy to signal user interface controls when outstanding pending changes are available or conflicts exist. You can also bind user interface controls directly to a conflict's properties, making a merge form quite easy to implement.
Data Management Services works over the HTTP protocol or the Flash Player real-time messaging protocol (RTMP). When using RTMP, a socket is left open between the client and the server, which makes communication more efficient and allows bidirectional communication. When using HTTP, polling is used for the client to receive messages from the server. You can set up a destination to receive messages over both channels and clients will try the channels you specify in order. This enables you to use RTMP in a way that will gracefully fall back to the slower HTTP when a direct socket cannot be made due to a firewall or lack of server resources.
The client code has the ability to revert all pending changes made on the client, which is also a useful feature to add to form-based applications so that the user can revert their changes.
Now that you know the basics of how Data Management Services works, read on to get more details and consider whether it is right for your next project and, if so, how to use it most effectively.