views:

106

answers:

5

UPDATED:

I have a desktop application, with the following components interacting:

  • Winforms UI.
  • Service (in-process C# class that contains business logic to actually do stuff).
  • Controller (C# class that coordinates events raised by the UI and calls service methods).

If a controller asks a service to do something, but that service needs something more from the controller first (i.e. data that the controller must use a UI to obtain from the user), how should the service get the controller to do so?


I'm comfy with the concept that

  • User communicates with
  • UI which communicates with
  • Controller which communicates with
  • Service component (not to be confused with a web service or out-of-process service) which communicates with
  • Data/Repositories which comm...

and so on.

However, with regard to the Controller communicating with the Service, what method is best for this? Should:

  1. Service methods be fairly fine-grained, and throw exceptions if something's not right so the controller knows whether to move on or tell the user something went wrong? Or...
  2. Service methods return objects that the controller can inspect to decide what to do next?

I like the first option, because the second could mean class-explosion, where you need a ServiceResult-style class for each Service method.

I ask because of course the Service component cannot tell the UI what to do, only the controller can, but the controller doesn't know what to tell the UI without getting some feedback from the Service.

What do you think?

+1  A: 

The first thing you need to keep in mind when designing the service layer/classes is to make them highly reusable. They should be atomic service objects which can serve not only your controller, but also any other clients in the future.

There are a few questions here:

  1. How does the controller find the service?
    If you desire to have highly reusable, extendable and ... service objects, the dependency between your controller and service classes should be loosely coupled. Avoid any tight coupling techniques. Instead, try to inject the service class to your controller (using an IOC container) and also you can use Service Locator Design Pattern.

  2. How to communicate with the service?
    Well, it depends on the size of your application. You may go for enterprise service technologies, such as Web Services, just use application services. Based on that, you define the protocol between the controller and the service.

  3. What data should the service return to the controller?
    Remember that the service layer should not know anything about the UI navigation. It's the controller's responsibility to decide where to go and what to do based on the response of the service object. Why? Imagine this service class needs to be used for another UI (let's say from web to flex, silverlight or desktop app). If you add navigation logic (as an extreme example, UI logic/formatting) in the service class, it won't be reusable for another UI system and also it won't be reusable for even other service classes and controllers inside the same application.

Bottom line, try to keep the service class clean of any navigation logic. The controller is really meant for that purpose.

Amin Emami
The controller gets the service via an IoC container. The trick is that sometimes the services can't go further because it needs more info, but it can't access the UI - it needs the controller to do that. So how should the service inform the controller that it needs to gather more information?
Neil Barnwell
If you think of the service as a complete and independent component with a certain responsibility (just reminds me of CRC), it may have a main (successful) scenario and some exceptions (alternative scenarios). In this case you're talking about the exceptions. This is a high level opinion. Now for the implementation, I like using exceptions is an elegant way. They are self-explanatory and very well managed when used properly in the applications. Even if you change your controller or have other clients for your services, it'll still work. Hope this is the answer to your question.
Amin Emami
A: 

See my question here

There are some missing details to better answer you question:

Is it a web or a desktop scenario?

If desktop - is it a disconnected scenario? (e.g. rich client with remote services) or does the client and service on the same machine?

If web - is it a classic web 1.0 application? (view is created as a page) or a rich client? (AJAX, Flex, SilverLight) where the controller sometimes can be in the client? (Flex)

All of this can significantly affect the decisions.

By the way did you omit the model in purpose?

Ehrann Mehdan
Desktop - see update to the question. This is all in-process - "service" in this context means a service object that contains business logic - the model is used by the service to do actual work, but the interaction between the controller and the service is what I'm really interested in.
Neil Barnwell
+2  A: 

Your best option is probably to use an event or delegate model - eg the controller subscribes to a 'needs more information' event and displays the appropriate form. Returning the additional data to the service could be done a few different ways, but would largely depend on the specifics of your design (eg are your services stateless etc)

Have you written unit tests for your service? If not, this process will probably help you clarify what you want out of your design.

Sam
A: 

I work a lot with MVC (though mine is the Java web app flavor), and I've often stumbled on some situations where it seems that the service needs to somehow interact further with the controller. But in every case, it's turned out that I've been doing something wrong in the controller or my service's interface is not flexible enough (i.e., fine-grained).

When the service doesn't receive enough data, is this due to the user providing invalid input? If so, then the controller should perform validation on the input and display the appropriate prompt or error message.

If it's expected that the user won't provide all the data at once, then the controller should be aware that the service won't accept the data as is. Therefore, the controller should manage the conversation with the user via the UI, then finally pass everything to the service in the end.

Or perhaps the controller needs to query the service throughout the flow of the wizard. In that case, the service's interface probably should be more fine-grained.

I would go with option #1 that you proposed. If the controller is capable of gathering all the required inputs from the UI and providing them to the service, yet it fails to do this, then the service ought to throw an exception. Using an exception is sensible because you're trying to pass invalid data to the service. (This doesn't change the fact that exceptions shouldn't be thrown to control program flow but rather to indicate that the controller actually screwed up in giving the service some invalid data.)

Jeff
A: 

Have you considered using the Model View Presenter?

Your UI views would implement interfaces, and these interfaces would be known to your services. This way, your services could call onto the UI to ask for more information to carry out their tasks. This is a kind of dependency inversion (pdf).

Jordão