1

Background

In much of the literature I've read online with respect to multi-layered architectures, many people describe how to create the simple application where:

  • UI presents static model objects to the screen.
  • Users can make one-off requests (e.g. send e-mail, commit payment).
  • A database transaction is typically made to return new information to show to the screen.

Often, the articles will describe these concepts while adhering to Eric Evan's Domain-Driven Design.

Problem

Despite the good resources online, there isn't much guidance on how to organize the software responsibilities for applications that need to be continuously communicating with external devices. Consider the following:

  • An e-mail client should continuously query a POP server to know when new e-mail has arrived.
  • A device monitor should continuously query the device's status variables to always display the live values (e.g an aircraft HMI might depend on the visibility of hundreds of variables at all times).

In both cases, the UI needs to be updated in real-time as changes occur in the external world. My question is about who should be responsible for initiating the requests to be made. I have come up with two potential solutions, the first involving a direct request from the UI, and the second utilizing inversion-of-control so that the UI can be notified:

  1. The UI layer can make use of background-threads (e.g. .NET's BackgroundWorker) to continuously make direct queries to application services which return ONLY the data required by the view in question.
  2. A background thread in an infrastructure service can continuously query ALL relevant information from an external device (perhaps by relying on some configuration) and send events/notifications (or call a callback) to other layers so that the UI can react. While personally not a fan of it, an event aggregator can be helpful to this end.

Architecture Differences

I think that solution #1 is better aligned with the examples I've come across, but my concern is that this is NOT a scalable solution. As the amount of continuous I/O increases, spinning off many background threads (for each view) not only causes us to violate DRY, but it degrades performance because of many, small I/O requests instead of few, large I/O requests.

How should we approach multi-layered architectures that need large amounts of continuous, real-time, communication?

Should the UI make direct queries on their own background threads, or should a single background thread and inversion-of-control be used?

  • 5
    To be fair, I think this is a common error people make with DDD. DDD is not a coding methodology; it is a design technique. DDD mostly concerns itself with the business domain and application architecture, not implementation details. It has nothing to say about threads. Feel free to use DDD to inform your design, but ultimately you get to decide how to best implement that design. – Robert Harvey Sep 14 '18 at 15:40
  • 2
    As a technology problem, there are several solutions available depending on the platform. Socket.IO and SignalR come to mind. – Robert Harvey Sep 14 '18 at 15:59
  • 1
    why dont streams solve your problem? – Ewan Sep 14 '18 at 17:08
  • 1
    "stream" in the generic 'stream of data' sense. It seems to me from the question structure that this concept has been dismissed from consideration, but there isnt any explaination of why. – Ewan Sep 14 '18 at 17:19
  • 1
    Your comment exposed some information that was left out. The architecture I'm after uses decoupled data sources, so it is very possible that streaming could potentially be used to talk to the external device. I've edited my question to point out that I'm after a comparison between direct requests from the UI vs. using notifications & inversion-of-control. – Nick Miller Sep 14 '18 at 18:06
1

Option 1 uses the BackgroundWorker and keeps the ViewModel responsive. This works fine in general, but can lead to problems in case the worker suffers has a high CPU consumption under stress conditions. Furthermore, repeatedly querying the infrastructure service doesn't sound to be a scalable and resource efficient scenario.

Option 2 on the other hand uses a very common architecture. Using threads for listening for a socket/device takes advantage of the waiting time that I/O usually implies without really using the CPU. The observer callback ensures that application uses CPU only when needed. So from the point of view of CPU consumption, this architecture should lead to better results.

Furthermore with option 2,you could handle scalability issues in the lower layers. If there are too many devices or a too high trafic, you could introduce some event streaming intermediate (kafka, pubsub or whatever) on other processing nodes that could eventually filter out only the relevant messages. Distributing the processing in such a way puts a very high bar for scalability. Your infrastructure service would then listen to the stream instead of listening directly to the devices. The next limit is then only the number of relevant incoming messages that your app layer could process.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.