P is for Proxy
In it’s most general sense, a service proxy is the representation of a service in a client runtime environment. From service to client, the proxy’s main functional values are service connection lifetime, flow-control, and marshalling content through data-contracts into the client’s runtime context.
The Ikosa proxy-model class encapsulates and implements the communication concerns for client-domain and can be considered part of the client’s view-model space, but with far less emphasis on view concerns, and much more emphasis on proxy concerns. The proxy-model is the bridge between communication and view-model space.
Session and Connection Lifetime
The proxy-model holds references to the three service clients (one for each Ikosa service: login, game-state and visualization), keeping communication to the service endpoints open. When “fatal” service errors occur, the proxy-model is capable of jettisoning the failing service client and initiating a new connection; in an attempt to keep client view-model impact low.
The server and proxy use duplex channel factories created in code for the service interfaces described in the application configuration files. All the service contracts have callback support (requiring duplex channels). Client-domain code in the proxy-model implements the callback interfaces to handle messages inbound from the service.
The proxy-model maintains a list of game characters as actor-view-models the client has logged into. If the user is a master user, a single master-view-model is created and held as a reference in the proxy-model. Typically each client will only log into one character, with the exception of the game master.
My focus right now is on the flow from model (in the service) to presentation (in the client). While a good deal of this flow is initiated by the client making calls to the service to pull some data, it is equally driven by changes on the server that need to be pushed to the client. The proxy-model class acts as the callback host for each of the service clients. When the remote service needs to send a notification to the client, the proxy-model contains code to handle it.
These notifications contain a mix of client activity responses, “out-of-band” changes to the overall game state triggered by another client, and changes contingent on changes to the game state “world” affecting the client’s actor. Directed information such as damage taken, results of actions, and additional information are also provided to specific actors.
The proxy-model receives the callback invocations and directs either all logged in actor-view-models to do change state, or directs information to specific actor-models.
View-Model Conformulation and Cross-Connecting
The proxy transforms data-contracts from the service into view-models that the client-domain can present. Much of the transferred context does not imply a complete invalidation of the client’s view-model state. Rather than getting a whole view-model refresh, only the relevant changing parts are pushed from server to client.
The proxy-model has the responsibility of taking the changing parts and updating the client-domain’s view-model to reflect the changed service-model state. This process is loosely a conformulation of the view-model to the changing flow of information.
By changing and keeping as much of the view-model stable as possible, the presentation and command bindings remain fairly stable, improving the experience for the user by keeping changes less impactful, while also keeping network bandwidth lower than a full refresh would require.
Many parts of the view-model also require access to code and structures that have been “normalized” into other services and common lookups. The proxy-model stitches these parts of the system together during large refreshes and incremental conformulation as much as possible so that the UI can rely on data-binding as much as possible and doesn’t have to rely on event callbacks.
The proxy in the client has to deal with the runtime host of the client, which has synchronization dependent rendering thread, message-bound interface threads, and other communication dependent threads all potentially running simultaneously. The proxy has to have a good deal of thread-dispatch (ie, “async” operations), locking (especially reader-writer locking) and concurrent data structures built into it to avoid threading violations, bad-read/writes and inconsistent state.