PolyglotSymposium / void

A fearless modern text editor in the spirit of Vim
MIT License
3 stars 1 forks source link

Rework request API into request/response messages #46

Closed Kazark closed 9 years ago

Kazark commented 9 years ago

There are three ways for services to interact given the current architecture, in order of preference. The items lower on the list sometimes have the correct semantics for the problem, in which case they are to be employed; but the items higher on the list are to be the preferred mode of thinking.

  1. Events - via messages
  2. Commands - via messages
  3. Requests - via injected functions

This updates #3 so that it also is using messages.

My motivation for doing that right now is that I think anything where the screen is refreshed as a result of an event or command that does not alter the state of the core editor is best handled with request/response semantics. This is because though there is an event that triggers this, it feels like domain leakage to let the core editor have knowledge that that view event. And thought I could use a command to request the necessary information---wait. I just said request. So doesn't that mean that the semantics are request/response?

The thing is, the view will fail to refresh unless it gets a response from the core editor. This means that it is dependent for doing its job on the core editor, and as such, it probably needs stronger semantics than commands. Commands are really supposed to be used for "do" instructions, not "get" instructions.

So given that I am working on some things that need request/response semantics, I chose to make this change because I am not comfortable with the inject functions strategy. It feels too coupled, is not flexible, and creates some complicated construction logic that is dependent on order of construction. Overall, too, as I'm evolving the code to have a more functional appeal, I am becoming increasingly suspicious of dependency injection.

The caveat is that this may be a premature abstraction. I've been working a lot in the infrastructure at work and have been having to do some pretty heavy magic to accomplish it. While appropriate for the particular project I am working on at work right now, this is probably inclining me back toward the magic code that I am naturally inclined toward but have rightly been taught to regard with suspicion.

To go further into the details of the advantages of this approach, it does not force you to unify the request handling services into a single function call. For example, if we end up with different services to handle different types of buffers, then services should be able to filter whether they service a request dependent on what the buffer ID is. Or closer to our current use case, if we are executing Python code in command mode instead of VoidScript (though that is also speculative, it is a goal) entirely different services can execute different types of code, without having to have a service which coordinates or manages them.

Also, it unifies the architectural communication options, and more strongly accents the fact that is a messaging architecture, and clarifies that services really shouldn't be referencing each other, and for the most part, shouldn't even reference each other's data types. On the flip side, the convenience of it may encourage a more orchestrated mindset, and I do not want this. Favor events.

Another thing I'm stepping toward here is the possibility that the ViewModel would not have a reference to the Core assembly at all, perhaps only to a message contract assembly. That's just one future possibility, however.

This should not be merged without discussion.

Kazark commented 9 years ago

One thing I haven't done yet that needs to be done---and this is part of the reason for not using a command message---is handling NoResponseToRequest<>. I think this is a powerful feature that could allow us to decouple the microservices to an amazing level. (For example, if you have a service that is providing you with an option but does not respond, you can fall back on a default.) That might be considered overcomplications, but I rather think it might be defensive programming. At the very least, if something that was expected to happen did not happen, we can report it to the user/developer.

mjgpy3 commented 9 years ago

Is this good to go?

Kazark commented 9 years ago

Yep, it is awaiting only your stamp of approval. :grinning:

Kazark commented 9 years ago

You may have to resolve a merge conflict though after you merge one of these. Or I can resolve it.

mjgpy3 commented 9 years ago

Feel free! Merge it up :+1:

Kazark commented 9 years ago

So after a little more time to look at this, I've figured out that it's basically crap. There are at least four major problems with it.

  1. Responses are constrained to response to a certain request type, but the request does not specify the type of the response. This leaves a big gaping whole in the whole concept of being able to either get a response or a notification that there was no response.
  2. It leaves things looking very disconnected and makes things hard to follow.
  3. It doesn't work well with envelope messages that I am using in the window/buffer case... which was the main catalyst for why I created it.
  4. When service A and service B both make a request to service C, they will both be notified of a response, with no way to tell whether or not they asked for that response. If the resultant action is not idempotent, this will trigger undesired behavior.

I need to design a solution that will take care of these. Honestly, I think I may just need to inject a request function. That will:

  1. Allow me to constrain the type of response, giving a true guarantee that you will get, well, probably the semantics should just be that you will get Some response, or None.
  2. Make the flow more obvious
  3. Hopefully allow me to solve the envelope problem
  4. Make sure responses only come back to the requester

Disadvantages of this suggestion:

  1. I don't like dependency injection. It is complex. However, what I currently have is already complex in another way. Maybe this will rightly encourage us not to go hog-wild with request/response.
  2. What else that I haven't thought of? I had really only thought of one of the four problems listened above when I initially did this. Argh!

:crying_cat_face:

mjgpy3 commented 9 years ago

Sad panda :panda_face: