1

I have an application (A1) which is divided into multiple tiers. Some tiers are used for user interactions, some are used to interact with different databases and some included the business logic. We have another third party application (A2) which sends a request to (A1) and A1 needs to response against the request. Below is the architecture of A1.

T3 (This tier receives the request from A2 application)

T2 (Business logic)

T1 (User Interface)

T2 contains all the business logic. The problem I am facing is when I receive request from the A2 application. I need to respond against request on the basis of some business logic which presents in T2. I can invoke the event from T3 that is subscribed by T2 but I have to get data from the event handler like below;

T3:

public Response CanStore(string materialType){
    //Invoke event and wait to get response from T2
    return response.;

}

T2: Subscribed the event of T3

 public async void canStore(object sender, EventArgs e){
     //Perform some logic and response result to T3
}

Is it possible?

Umer Waheed
  • 4,044
  • 7
  • 41
  • 62
  • 1
    When you say "Tier" are these logical tiers (ie, assemblies within the same running process) or separate services using something like HTTP or gRPC? – Jamiec Oct 08 '20 at 10:57
  • Yes. It is same running process – Umer Waheed Oct 08 '20 at 11:01
  • but the A2 is a different service/application from where we receive a request in A1 application in T3 tier – Umer Waheed Oct 08 '20 at 11:02
  • Why do you use events in the first place? Is there a reason for that? Can the eventHandler `canStore` somehow tell for which `materialType` it has been raised? – Fildor Oct 08 '20 at 11:03
  • I agree - events don't seem like the right mechanism here. This might help: https://stackoverflow.com/questions/12451609/how-to-await-raising-an-eventhandler-event/30739162 – Jamiec Oct 08 '20 at 11:04
  • @Fildor These tiers are in different classes. CanStore is not an event. It is an exposed API method. – Umer Waheed Oct 08 '20 at 11:10
  • 1
    So why is T3 -> T2 using an event at all? Why not just call a method and get a response? – Jamiec Oct 08 '20 at 11:11
  • Because T3 doesn't have a reference for T2. – Umer Waheed Oct 08 '20 at 11:13
  • @umer I am aware of that. The communication between T2 and T3 is events-based, though for some odd reason. And this makes all this unnecessarily complicated. – Fildor Oct 08 '20 at 11:13
  • _"Because T3 doesn't have a reference for T2._" Why should it need one? – Fildor Oct 08 '20 at 11:14
  • 2
    @Fildor If it did you could make a direct call from application receiving event to the business logic which is the way I would have had this. Im not sure why you'd have it the other way round – Jamiec Oct 08 '20 at 11:15
  • It's like T2 is child and T3 is parent Tier. T2 can call T3 methods because T2 has the reference of T3. But T3 doesn't have T2 reference. – Umer Waheed Oct 08 '20 at 11:16
  • @Jamiec Ah, darn. I confused T2 and T3 ... my bad. You're right. – Fildor Oct 08 '20 at 11:16
  • 1
    _"Because T3 doesn't have a reference for T2."_ ...why not? And what _is_ T3? What is its purpose? Is it an API of some kind (as an alternative to using the user interface in T1 perhaps?)? That seems likely, as you're saying it can receive requests from other apps. If so then it would make sense for it to have a reference to the business logic tier. Why does T2 need to reference T3 though? That seems the wrong way round. Does T3 also have some other purpose as well? If so, then consider splitting the component into 2 (or more) which have specific purposes. – ADyson Oct 08 '20 at 11:16

2 Answers2

4

It sounds to me like you have your architecture the wrong way round

If T2 has business logic, and T1 is a User interface which presumably needs access to the business logic and T3 is an application which receives messages from an external party and also needs access to the business logic, then both T1 & T3 need a reference to T2.

Then this is just a simple bit of Dependency Injection of the business logic into T3!

public class T3Service
{
    private readonly IT2BusinessLogic businessLogic;

    public T3Service(IT2BusinessLogic businessLogic)
    {
        this.businessLogic = businessLogic;
    }

    public Response CanStore(string materialType)
    {
        var t2Response = businessLogic.CanStore(materialType);
        // Do what you like to build response to external service
        return response;

    }
}
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • You are correct. At first, I argue with the person who made this architecture. But somehow there are other limitations because this is a very huge application which contains around 20-30 layers. – Umer Waheed Oct 08 '20 at 11:22
  • 2
    @umer then you've asked an over-simplified question about a complex problem. I can only answer based on the info you give. – Jamiec Oct 08 '20 at 11:23
  • 2
    @umer Raise and waive a bright red flag, _now_. This sounds like you are tiptoeing and patchfixing around an overall broken design. Going on like this will only make it worse. – Fildor Oct 08 '20 at 11:23
  • Ok. I agreed with you all but again is there any possibility to wait for the event handler to response. – Umer Waheed Oct 08 '20 at 11:26
  • 1
    @umer Probably yes, but if you cannot relate response to request, it's not much of use. – Fildor Oct 08 '20 at 11:29
1

Aside from the architectural issues and assuming you can modify T3 and T2, you can workaround abusing the EventArgs with some custom EventType. NOT my favorite but could solve your issue.

Let T2 manipulate the EventArg to store the required result inside. After completion of your Eventhandler the calling site T3 can fetch the result from the eventArg.

Something like

public Response CanStore(string materialType){
    //Invoke event and wait to get response from T2
    myEvent.Invoke?(sender, myCustomEventArgs);
    await myCustomEvent.Completion.Task;

    return myCustomEvent.ResponseFromSubscriber;
}

with myCustomEvent extending your current Event with two Properties,

MyCustomEventArgs: MyCurrentEventArgs
{
    // makes your Event "awaitable";
    TaskCompletionSource<bool> Completion{ get; } = new TaskCompletionSource<bool>; 
    Response ResponseFromSubscriber{ get; set; } // As you need 
}

and the subscriber

public async void canStore(object sender, EventArgs e){
    //Perform some logic and response result to T3
    if(e is MyCustomEventArgs myCustomEventArgs)
    {
        myCustomEventArgs.ResponseFromSubscriber = new Reponse(); // Your whatever 
        myCustomEventArgs.Completion.SetResult(true); // Triggers the Task completion for your awaiting EventInvoker
    }
}
Matthias
  • 134
  • 1
  • 9
  • Is there any harm to use this approach? – Umer Waheed Oct 08 '20 at 13:56
  • `public Response CanStore(string materialType){` you cannot use `await` in here ... – Fildor Oct 08 '20 at 13:59
  • I just noticed the async EventHandler and went that route to show how to make it fit in an async enviroment. Not necessary at all and not threadsafe. Not even save to multiple subscriber all messing around with the EventArgs. Just a "make it work now" independent of good architecture. – Matthias Oct 08 '20 at 14:55