Separation of Concerns is Impossible in Service Fabric



  • We have two things: Registration and Implementation1.

    Implementation1 references Registration, so it can send the appropriate instances of some objects, as XML.

    Registration picks up Implementation1's data, but does not reference it, because that would be circular.

    The problem is that at certain times, things happen in Registration that require a message to be sent to the user that is implementation-dependant, but that requires information from a specific point in time in Registration as well. Since the only communication is through serialized objects, I can't simply define an interface in Registration and let my implementation implement it, because Registration doesn't know the type and so can't deserialize it. I believe this may also eliminate delegates.

    I'm asking this here, but no one can probably help me. But if you can, please do.



  • I suggest a high-temperature flame, possibly a magnesium fire.



  • @magus Could the Implementation pass a callback to Registration?



  • @magus said in Separation of Concerns is Impossible in Service Fabric:

    I'm asking this here, but no one can probably help me. But if you can, please do.

    I think we kind of need more details. It looks like what should resolve the problem would be for Implementation1 to ask Registration for the data it needs, include it in its own object and send that to the user?

    If the problem is that you need to return the message from Registration, then you can either wrap the object you're receiving from Implementation1 without inspecting it at all, or you can extract your types to a separate assembly/package/etc. and reference that from both Registration and Implementation1.

    In general, if you have two projects that consume each other's objects, what you generally do is to separate the object definitions from the code that creates those objects.

    If for some reason you can't do that, just have Registration have its own definition of the object separate of the Implementation's definition? If you're going full microservices then Implementation should have a stable API, so it shouldn't be too problematic for that API's consumers to define their own models.



  • @scholrlea said in Separation of Concerns is Impossible in Service Fabric:

    @magus Could the Implementation pass a callback to Registration?

    Not as far as I can tell. That would be the obvious solution.

    @maciejasjmj said in Separation of Concerns is Impossible in Service Fabric:

    I think we kind of need more details. It looks like what should resolve the problem would be for Implementation1 to ask Registration for the data it needs, include it in its own object and send that to the user?

    Registration knows how to send things, and has full control over when things are sent. It only gets data from Implementation1 once, when it discovers it.

    @maciejasjmj said in Separation of Concerns is Impossible in Service Fabric:

    If the problem is that you need to return the message from Registration, then you can either wrap the object you're receiving from Implementation1 without inspecting it at all, or you can extract your types to a separate assembly/package/etc. and reference that from both Registration and Implementation1.

    The problem is that the number of implementations is unbounded, and they could all need different message structures with the same input data, but Registration can't handle that in any way, because it has to deserialize the instance successfully.

    This is fundamentally the exact scenario where a callback is the ideal approach. I just can't DO that.


    My plan now is to convince them to let me say that the message structure is generic, and the implementations will just fill in specific fields. This architecture just causes me pain.



  • @maciejasjmj said in Separation of Concerns is Impossible in Service Fabric:

    In general, if you have two projects that consume each other's objects, what you generally do is to separate the object definitions from the code that creates those objects.

    Since the solution uses XML, I would hope there's already an XSD that provides that service.

    You're missing a lot of information here (such as what "references" implies, and why you can't have circular ones-- REST APIs from two apps can call each other no problem.) But here's my recommendation:

    • Registration

    Modify database so it keeps a log of messages intended for Implementation1. Create new XML object to serve up these message logs in an XML format matching the existing XML formats you're using as closely as possible.

    • Implementation1

    Whenever Implementation1 talks to Registration, have it send a "give me the log of messages intended for me/my user/this app/whatever" request to Registration. Then write code to receive and parse-out those communications.

    Still a lot of code, but it keeps your concerns pretty separated.



  • @blakeyrat said in Separation of Concerns is Impossible in Service Fabric:

    Since the solution uses XML, I would hope there's already an XSD that provides that service.

    It's generic DataContractSerializer stuff. We don't think about the details of the XML mostly, except that anything we do has to be serializeable and deserializeable.

    @blakeyrat said in Separation of Concerns is Impossible in Service Fabric:

    You're missing a lot of information here (such as what "references" implies, and why you can't have circular ones-- REST APIs from two apps can call each other no problem.) But here's my recommendation:

    I'm meaning generic C# project references in this case. I wish we were using REST, or a database, but we're doing something altogether weird and frustrating.


    In general, your suggestion here is awesome, and if I could do it, I probably would.



  • @magus said in Separation of Concerns is Impossible in Service Fabric:

    In general, your suggestion here is awesome, and if I could do it, I probably would.

    I don't understand why you can't. Can you elaborate?



  • @blakeyrat To the best of my knowledge, there is no central storage of messages, partially because there's no central storage of anything. Things are being saved on actors, which has some peculiar effects. Additionally, all of our messages use a weird proprietary bubbling system developed by the team before us.

    It's possible that the service responsible for messaging preserves some of that data, but the flow you're talking about is also not quite what we have.

    Basically, at a point in time, a user will do something in Registration that requires a message to be sent to them or another user, with some information about them. The text of this message differs based on the implementation they selected, and therefore where the user data goes in the message changes. Which means, to me, that the message needs to be generated right then. However, the only communication between an implementation and Registration happens when Registration first encounters that implementation, and never again. It gets serialized data, which it uses from then on.

    So I have data, but I need logic.



  • @magus said in Separation of Concerns is Impossible in Service Fabric:

    To the best of my knowledge, there is no central storage of messages, partially because there's no central storage of anything.

    Uh. Ok.

    @magus said in Separation of Concerns is Impossible in Service Fabric:

    Things are being saved on actors, which has some peculiar effects.

    What's an "actor"? I mean in concrete terms.

    @magus said in Separation of Concerns is Impossible in Service Fabric:

    However, the only communication between an implementation and Registration happens when Registration first encounters that implementation, and never again.

    Right; but why can't that be changed?



  • @blakeyrat said in Separation of Concerns is Impossible in Service Fabric:

    Uh. Ok.

    @blakeyrat said in Separation of Concerns is Impossible in Service Fabric:

    What's an "actor"? I mean in concrete terms.

    A Service Fabric Reliable Actor can save information about it's state. Somehow. And we're using that, along with some kind of storage of historical changes to objects instead of a database. Despite it not fitting our needs in the slightest.

    @blakeyrat said in Separation of Concerns is Impossible in Service Fabric:

    Right; but why can't that be changed?

    Time, architectural consistency, people not listening to me and doing the most complicated thing they can imagine at every opportunity...

    There are ways I could set up a communication between the two, but it would involve a day of code in 2-3 projects every time we add an implementation. Something that, in a sane world, would be a simple method call or API endpoint. But no, that would be simple. We just can't have readable, normal, understandable code.



  • @magus said in Separation of Concerns is Impossible in Service Fabric:

    A Service Fabric Reliable Actor can save information about it's state.

    But not a list of things that Implementation1 needs to know?



  • @blakeyrat I think accessing that would be the hard part. By design. Design I disagree with vehemently. And it'd be a giant log of all messages ever, most likely.


  • Discourse touched me in a no-no place

    It sounds like you are wanting a system that looks like a simple procedure call yet which needs to make some kind of callback. That makes it very much not simple! Alas, the best you can do is to probably return some kind of generic description document that says why it couldn't perform its operation and what extra information the implementation thinks the client of the system should provide in order for the call to be successful.

    Either that or you need to do some careful sequence diagrams to determine better who needs to know what and when. Which gets messy!



  • @dkf I managed to convince them that it's silly to allow these messages's content to vary in more than details, since they're in a user registration process. Averted.



Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.