pnxtech / hydra

A light-weight library for building distributed applications such as microservices
https://www.hydramicroservice.com
MIT License
645 stars 54 forks source link

Run in browser? #193

Open flowt-au opened 6 years ago

flowt-au commented 6 years ago

Hi. I am exploring using microservices as a way to compose a web-app ie run it in a browser. I am transitioning from "old school" monolithic to microservices and am fairly new to Node etc.

The app has lots of business logic in the client with data storage a mixture of server via REST/JSON and local data via pouchdb.

The initial iteration was MVC which got messy pretty quickly. Next was MVVM which was much better but still coupled. Then I tried a global event bus as a way to decouple which was an improvement but still didn't feel right.

I want a system whereby I can use my Gherkin specs to run the app "headless" ie only run the models with no views at all to test the business logic. Then implement the model states as view representations with whatever renderer, persistence, logging, etc I go with.

So, in theory microservices fits the bill with its loose coupling, composition, etc.

Could I use Hydra in the browser? Can I simply Browserify it (or similar)? Is it even a good idea?

Any thoughts or directions would be helpful.

Thanks, Murray

cjus commented 6 years ago

@flowt-au super interesting! Hydra isn't designed to run in the browser. The first issue that comes to mind for me is the singular dependency on Redis. If your goal is to use web browser technology that would be one thing - but if the intent is also to have clients running in browsers by end users - then each would need access to a shared Redis instance (or cluster) over a public Internet. That would be difficult to secure. The second issue is the use of the NodeJS event model. Hydra is derived from the Node event emitter.

Your project's specific goals can help to further determine what's feasible. Consider the above and feel free to continue to brainstorm. I find the idea definitely intriguing!

flowt-au commented 6 years ago

@cjus Thanks Carlos,

Basically, the app is for small to medium organisations for authoring and sharing internal documents across the net. I wont bother with details here - suffice to say it does things that go beyond the scope of say Google docs and the like and is quite internally complex. We are in Beta at present and have happy users which is good.

However, I am looking down the path at needing to reorganise the code base in order to implement the other 90% of what I have in my head!

Anyway there are a few variants, all written in html/js/css as single page apps, all talking to the same backend MySql server via http / REST. A pretty standard approach.

  1. The full version is packaged into an executable for Win, OSX & Linux using nw.js (Node plus Chromium) giving access to the user's local resources eg file system, etc.

  2. A subset version for web browsers (ie without the local file system stuff) for both desktop and mobile devices (WebApp). The mobile version is essentially a reader.

  3. In the pipeline: packaged apps for Android and iOS.

Now, all of this is done with a single code-base for the business logic with "views" / state representations that depend on what the context is

So, in my research about a better way to architect the code base it occurs to me that microservices in the app are an obvious theoretical approach so that the various internal functional modules can send messages rather than being coupled. This will allow me to "plug in" the new modules as they are written, rather than having to "wire them in" via function calls where module X needs to know that the state of module Y has changed in some way.

Maybe I need to revisit my global event bus idea and rework it for messages and pattern matching. Maybe use a message queue instead.

I guess in a nutshell I am thinking "Why not architect the client apps (front end) in the same way that the "back end" can be architected - as a collection of microservices?"

There is this idea: https://micro-frontends.org/ which is an interesting way of building the view components as pseudo-microservices but not the business logic which is actually my greatest concern.

Thanks for your encouraging reply, Carlos.

Murray

cjus commented 6 years ago

@flowt-au seems like micro-frontends approach could benefit from microservice backends which in addition to API might also serve portions of the component based UI. At least if I'm understanding it correctly. So in that way, hydra isn't ported to the front-end - but rather runs in the backend in service of micro-frontends.

flowt-au commented 6 years ago

@cjus Yep, that could be part of it. And I am still playing with the idea of making the front-end a composition of microservices.

Take a simple example: the title of a document is changed by the user - eg an internal Policy document. This title is listed on various UI components - the actual number of which depends on what bits of the app the user has currently open. The change is also sent to the server. So, to notify the other UI components in that user's instance (eg a select box somewhere, or a window title, or ...) I could fire a custom event on the document model (in the business logic sense of the word "model", not the dom) and all the components, including the relevant persist-to-server process, could hear that and act accordingly.

Or, in microservice language, I send a message instead of firing an event. They are probably the same thing in a way. I guess the difference is that the event would contain a message as json rather than the list of arguments as they normally do. Maybe I am just overcomplicating it and all I need is a global event bus with all events as json packets (or just JS objects).

Another thought with using messages is that a server side event that implements a push could then send that event as a message with the same signature on both client and server sides. So, when the document title changes in one user's instance and is persisted, the server could broadcast that change by pushing the message to all active users who get the message, not caring whether it came from themselves or from another user via the server, then all "listener" services do their thing.

Still musing. Thanks for being a sounding board. Regards, Murray

flowt-au commented 6 years ago

PS: Just to be clear, I am not asking you to port Hydra. I am just thinking about whether microservices on the client side is a good idea or not, and if so, how it might be implemented. Thanks again.

cjus commented 6 years ago

I do think that applying microservice ideas on the client-side is a good idea. Although admittedly I hadn't considered this before your post. Most of my current work has a backend focus.

Have you considered server-side rendering and the implications that would have for front-end microservice like composition?

flowt-au commented 6 years ago

@cjus Yes, I have considered the server-side rendering issue. Do you mean rendering views? If so, that is only a small part of the bigger picture. For me, the more important issue is the way the business logic is composed. I guess my previous post was mostly talking about a view (the policy document title change) but there is still a lot of business logic associated with even that seemingly simple requirement:

Sometimes that business logic will be on the server and the UI rendered accordingly; eg the server will not even send data (or ui) if the user doesnt have permission to see it. Sometimes it will make more sense to embed the business logic on the client side. The reason is that the applicable rules can be quite complex and having to continually ask the server for new UI or data based on what this particular person wants to do within their permission scope would be messy and slow. Better to have the client side make that decision locally since they have all the data already (and can work offline if there is a local cache). It becomes a business rule consistency issue rather than a "permission" issue.

The business rules / features / scenarios for the domains are what the specs determine in consultation with business owners. The domain "models" are then written and tested, sometimes in the absence of the UI at that stage. So the UI part is just the representation of the model which will change as a result of the application of the rules applied to that user in that specific instance of the app.

My idea would be that all the client side domain models and their various forms of state representation (UI, persistence, local cache, etc) could be separate microservices using some kind of messaging / event system to make them loosely coupled for all the reasons that microservices are good idea. The Tao of Microservices suggests that the messages are what need to be modelled and specified rather than the model entities themselves - I find that perspective intriguing and it feels like it has validity. But I have no experience yet.

So, at this stage I will go and play with these ideas. If I come up with anything useful I will report back! :-)

Thanks again for your time in reading and thinking about this. Murray

cjus commented 6 years ago

@flowt-au I do find the ideas more than intriguing. I believe they're actually the future of front-end development. So I was thinking of handling more than just view rendering server side. It's also the best place for logic and "engines".

Richard Rodger does advocate a "message first" approach - which I'm also in favor of. It's the pattern matching I'm not entirely on board with. Have you checked out http://senecajs.org ?

flowt-au commented 6 years ago

@cjus Yes, I have looked at Seneca. And others like Cote.

I have also been looking at WebWorkers which uses MessagePort under the hood.

I am thinking that using a message pattern match (like Seneca or Cote) and having each pattern in a web worker might be a way to go. To reduce the overheads of script loading there is this interesting approach using Gulp.

Still thinking ...

flowt-au commented 6 years ago

@cjus Yes, I have looked at Seneca. And others like Cote. All only run in Node (not browser).

I have also been looking at WebWorkers which uses MessagePort under the hood.

I am thinking that using a message pattern match (like Seneca or Cote) and having each pattern in a web worker might be a way to go. To reduce the overheads of script loading there is this interesting approach using Gulp.

Still thinking ...

PS: Also investigating WebSockets as a way to "push" messages to clients.

flowt-au commented 6 years ago

And this: https://www.youtube.com/watch?v=vm_oOghNEYs https://bitsrc.io/