Re: Client/Service relationships & Flow of Requirements.
- From: "H. S. Lahman" <h.lahman@xxxxxxxxxxx>
- Date: Mon, 29 Jan 2007 20:29:33 GMT
Responding to Carter...
The client/service relationship does, indeed, reflect a dependency
between the client and the service. However, that dependency is
reflected in a flow of requirements rather than a function calling
hierarchy. That is, the client(s) define the requirements for the
service. In particular, communications between the client and service
subsystems are entirely orthogonal to the flow of requirements. This is
an enormously important point in understanding the structure of OO
applications.
While I'm mostly in agreement with HS sentiments there are a number of,
ahh, umm, shall we say, "points of clarification" that need to be made.
1) Whilst requirements may, as HS says, "flow from client to Service",
this is a negotiated flow.
At any stage the owner of the Service is entitled to say one or more of
the following...
a) That requirement doesn't fit into my single clear responsibility. That
requirement is either your responsibility or that of another, possibly as
yet unwritten service.
b) Fulfilling that requirement requires state that I do not and should
not own. Ask it of the owner of that state, not me.
c) I have many other clients, adding that requirement will break my
obligations to them. Go make another plan, either implement that
requirement yourself, or possibly creating a new service that implements
that requirement, possibly using me to implement the bulk of the work.
Remember that the blog category argues that three things must play together in application partitioning: subject matter, level of abstraction, and dependencies. One starts with defining a subject matter for the service. That subject matter will have a specific semantics with respect to the problem in hand. That tailoring of the subject matter semantics is defined by allocating specific problem requirements to the subject matter because they logically belong to the subject matter.
For example, one client needs a statistical service to compute mean and variance for a data distribution while another client needs a statistical service to compute correlations between data distributions. The semantics of what a statistical service subject matter /is/ will be the same for both clients. Consequently, the /sorts/ of services it provides will be readily recognizable within the problem scope. But the specific requirements addressed by the subject matter will depend on the problem in hand. That only indirectly depends upon which sort of client one needs to solve the problem.
The flow of requirements enters the picture in two ways. The first is identifying the need for the subject matter (service) in the first place. One either needs the subject matter or not and that is driven by the needs of the client. Once the need for the subject matter is established, though, the specific problem requirements that logically belong to it should be quite clear. (Note that the client may have different sets of requirements resolved by multiple service subject matters.) So the client only indirectly drives the requirements in this sense through the subject matter.
[When there are multiple internal clients, they will all expect a certain class of requirements to be addressed by the service. But each may expect a somewhat different class of requirements. The set of overall requirements will be determined indirectly as the union of the individual clients needs (e.g., mean and variance on one distribution vs. correlations). So what the specific client is doing will indirectly determine which /sorts/ of individual services will be needed from the subject matter and that will usually be quite helpful when allocating problem requirements to the subject matter. That's because the problem requirements in customer terms may be much coarser (and may not even mention statistical services!).]
However, the second way the flow of requirements is relevant is in defining the interface for the service subject matter. That depends directly on how the client wants to use the service subject matter. Thus the interface is typically negotiated between the client and service. To the extent that the interface reflects the subject matter requirements, the client's requirements on the service drive the interface design. But the practicalities of implementation and the need for large scale reuse are likely to "tailor" the details.
Alas, an interface is essentially a syntax for accessing the semantics and for any complex semantics one can have multiple syntaxes. Thus the interface negotiated in the original context may not be appropriate for a reuse situation with a different client. That leads to the notion of two-way Facade-like subsystem interfaces where one can apply "glue" to resolve syntactic mismatches without touching the implementations of either subsystem (also described in the blog category). That "glue" effectively represents a renegotiation of the interface in a reuse situation.
Note, though, that in a reuse situation the requirements on the service subject matter semantics must necessarily be the same (or at least a subset) even though the clients are different. Otherwise the subsystem will not be reusable; one can fix syntactic mismatches (access) but not semantic mismatches (requirements). IOW, the problem requirements are intrinsic to the subject matter once the nature for the subject matter is defined. So the client defines what subject matter is needed, but the specific requirements are allocated on the basis of the nature of the subject matter and the problem in hand, so they are not negotiated. However, the interface to those requirements is negotiated.
So what happens when a reuse situation has additional /problem/ requirements than the original context? (The existing requirements can't be different because they are intrinsic to the subject matter semantics; only the set of requirements needed for the new problem may increase.) One needs a new subsystem that addresses the superset of problem requirements across reuse situations. However, that new subsystem will still have the original subset of requirements for the original problem context so it can be substituted in the original context.
So to <finally> specifically address your issues...
1a. The requirements are intrinsic to the subject matter. That will usually be quite clear once the subject matter is identified properly using the techniques described in the blog. However, it will often be helpful to consider how each client wants to use the subject matter when doing that allocation.
1b. I agree. However, I think that is implicit in defining the nature of the subject matter (e.g., "subject matter X will process data set Y.") IOW, I see that as mostly a matter of interface negotiation.
1c. For a given application problem, all the requirements that are necessary to solve the problem and are logically relevant to the subject matter need to be addressed by the subject matter semantics. The individual internal clients simply provide the union of requirements for the subject matter semantics. As discussed above, reuse introduces additional complexity in that the reuse contexts may introduce /additional/ requirements. But the original set will be invariant.
2) This is perhaps a semantic quibble. A service may well have
preconditions, which are a requirement on the client to meet before
invoking the service. ie. There can well be a flow of requirements from
the service to the client.
True. But I think they are different requirements than those on the service; DbC just validates them in the service. That is, I would argue that processing state data (1c) provided by the client will be part of the basic service requirements. The requirements allocated to the service subject matter will be about what the service does with the data. However, data integrity and timeliness are additional requirements beyond those dictating what the service does with the data. In fact, data integrity and timeliness are requirements that logically should be allocated to the client as part of the nature of its subject matter. IOW, requirements on the client subject matter dictate it should create good data. DbC preconditions just formalize that those requirements were properly allocated to the client rather than to the service using a contract motif.
3) I regard the dependency tree as clearly involving the call graph in two
ways.
a) If the service explicitly names compile time symbols of the client, do
not be surprised if it becomes very difficult to ever reuse that service.
(The old cry, "Waah! We can't do software reuse, it's too difficult.
Yes, well. You didn't write reusable software so don't cry now that you
can't get software reuse.) If enough symbols are involved change
"difficult" to "impossible to reuse", even within the same executable.
My response is: Don't Do That. B-) In fact, using the interface approach I advocated on the blog for subsystems, it is impossible to get into that situation because the interface will be pure message-based (i.e., {message ID, <by-value data packet>}). As a result no element of the implementation of one subsystem will be known to the implementation of any other subsystem.
[The overhead of doing that at the object level tends to be prohibitive, which was one reason why object reuse never achieved the hubris of the early '80s. OTOH, it is usually quite practical at the subsystem or component level for large scale reuse.]
b) As an empirical observation, rather than a hard and fast rule. But one
with a high probability of being valid. Service code that makes explicit
synchronous function calls to the client often create thread races and
subtly smash invariants.
That is exactly why one prefers pure message interfaces for subsystems, especially event-based interfaces.
BTW, I am uncomfortable with the use of 'call graph' in the first sentence of (3). First, the idea of synchronous calls to an API is not a real good way to think about message-based communications in an OO context. One is better off thinking in terms of asynchronous announcement messages for inter-subsystem communications. If the interface is designed that way at OOA/D time, it can always be implemented synchronously during OOP. But if it is designed around synchronous calls, it may not be possible to implement it in a truly asynchronous and/or concurrent environment.
Second, my original context point was that communications are orthogonal to dependencies among an application's subsystems. When one represents the partitioning of an application in a UML Component Diagram and shows the dependencies, that has nothing to do with who initiates messages. For example, once one is outside the CRUD/USER realm, communicating with the software user via a UI is usually encapsulated in a low level service subsystem that will be on the bottom of the dependency graph. Yet almost all the communications will be generated by the UI subsystem to the client subsystems above it because the user is ultimately running the application. In that case the "call graph" flow will be essentially opposite to the directed dependencies in the Component Diagram.
Thus I prefer the hard and fast rule that the service be entirely ignorant
of the client, and the client _only_ knowledgable about the interface of
the service. So much as a single upward reference is forbidden.
I agree, but I would take this much further. Neither the client NOR the service should have any knowledge of each other's implementations. Note that in the blog I referred to two-way interfaces. When a message is sent outside a subsystem, the subsystem implementation invokes an output interface. That interface remains fixed no matter what context the subsystem is used in. That interface (think: GoF Facade) invokes the input interface of the target subsystem. (Any "glue" code to resolve syntactic mismatches in reuse situations goes in the the implementation of the output interface.) Thus the implementations are completely decoupled.
There are saner ways involving link time, boot time or run time
registration of clients that work better, often using the pattern that
both the client and the service depend on a common stateless
Javalike interface.
Sorry, but you've lost me here.
*************
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
.
- Follow-Ups:
- Re: Client/Service relationships & Flow of Requirements.
- From: John Carter
- Re: Client/Service relationships & Flow of Requirements.
- From: John Carter
- Re: Client/Service relationships & Flow of Requirements.
- References:
- Client/Service relationships & Flow of Requirements.
- From: John Carter
- Client/Service relationships & Flow of Requirements.
- Prev by Date: Re: Alleged Relational Stumper
- Next by Date: Re: Booch's book feels too philosophical rather than practical?
- Previous by thread: Client/Service relationships & Flow of Requirements.
- Next by thread: Re: Client/Service relationships & Flow of Requirements.
- Index(es):
Relevant Pages
|