2

I'm working in Java Spring, and I have typical service and repository layers. The repository grabs a JSON; passes it along to the service; service maps the repository response to a DTO.

I also need to perform some event logging afterwards (send these events to an auditing REST service), which requires some properties that are not part of the DTO, but rather part of the repository response object. Note that these particular properties are actually used as part of the business logic to perform the mapping.

So, you may say that for obvious reasons, just go ahead and use the repository response object to perform these event loggings since some of properties are not present in the DTO object.

However, mapping of repository response to DTO requires quite a bit of calculations, business rules, etc. which now the event logging also needs if I only use the repository response. In other word, I have to again execute those same business rules which I performed during mapping, and use them for event logging process. All because the event logging needs a few properties that repository response object has but not the DTO object.

To make matters worse, the same DTO object is used by a few controllers to ultimately send results back as part of the JSON.

There are two solutions I pondered:

  1. Include those needed properties in the DTO object, so they can be used by the event logging process, but go ahead and mute them during marshalling/demarshalling with the JSON library. If additional event logging scenarios are introduced with more missing properties from the repository response, then I have to keep adding them here
  2. Do a portion of logging that requires those missing properties from DTO while performing the mapping in place. The issue is, now I'm heavily coupling the operation of mapping from one object to another with partial or complete event logging which will be terrible for unit testing and general laws of universe. Also, as additional event logging scenarios might come along, this coupling becomes even deeper and deeper

I wanted to know if there are other solutions/design patterns that can be more sensible, extensible, and maintainable that I can utilize?

1
  • I tend to offer outlandish ideas to break the mold, but I don't fully understand your situation enough to help in that way. Some questions: 1) Do you need to log both read/write operations? 2) Do you need to log about all entities? 3) Are you talking about logging the web-service calls themselves? (URI, IP, etc..). Thanks – Todd Dec 29 '20 at 4:36
0

Given that you want to keep doing your logging where you're doing it, rather then move it to where all this stuff is already known, I'd go with option 1.

What I'd do a little differently isn't to teach something to "mute" them before marshalling. That smacks of knowing more than I want to know. I'd spin up another DTO for logging and send both together in a collection. Then you can hand marshalling what it's always expected and logging can have whatever it's extra little bit of info is without anything but logging knowing whatever that is.

This is an old idea that has been used on errors when you don't want to handle them right away and don't want to branch your process with an exception but you still want the error to get expressed. You just pack up the error and stuff it in the typical dataflow. You can find a great lesson on that here.

Now if you don't like casting when you take things out of a collection I'm afraid you have a little bit of work ahead of you because Java still doesn't offer a heterogeneous collection in its standard library. Joshua Bloch (author of much of Java) has you covered. Either follow that link or pick up a copy of his book: Effective Java.

0

The better is option I found was to create a new object that extends an aggreggate portion of my DTO object (ObjThatIsNeededForBothUIAndLogging ) and placed those extra properties there (ObjWithExtraProps), and use that for my logging purposes. I can then copy properties needed for UI from ObjWithExtraProps to its parent's (ObjThatIsNeededForBothUIAndLogging) minus those extra properties when needed, e.g.:

MainServiceObject (DTO) ---- Inherits ----> ObjectUsedForUI (JSON)
       |           \                                |
  Aggregate         \                            Aggregate
       |             \________ Old Aggregate _____<>|
       /\                                           /\
       \/                                           \/ 
   ObjWithExtraProps ------ Inherits -----> ObjThatIsNeededForBothUIAndLogging  
0

I'd favor option 1, but with a different approach on how to sort the data. For the sake of example, I am assuming this is your current DTO:

public class MyDto
{
    public string Data1 { get; set; }
    public string Data2 { get; set; }
}

and your event logging needs access to an additional value called string SecretData.

  1. Include those needed properties in the DTO object, so they can be used by the event logging process, but go ahead and mute them during marshalling/demarshalling with the JSON library. If additional event logging scenarios are introduced with more missing properties from the repository response, then I have to keep adding them here

From your description, I infer that what you'd end up doing is something like:

public class MyDto
{
    public string Data1 { get; set; }
    public string Data2 { get; set; }
    public string SecretData { get; set; }
}

But what I would suggest you do is to pre-emptively subdivide your data into two subclasses, i.e. the " result" and the "event metadata".

public class MyDto
{
    public Result Result { get; set; }
    public MetaData EventMetaData { get; set; }

    public class Result
    {
        public string Data1 { get; set; }
        public string Data2 { get; set; }
    }

    public class EventMetaData
    {
        public string SecretData { get; set; }
    }
}

Note: I'm using nested classes here but that is not inherently required. Feel free to separately define these classes.
Note 2: Class names can definitely be improved, these are just named for the sake of simplicity.

This keeps things self-contained. Your service receives the MyDto object, uses the EventMetaData for its own internal purposes, and returns the Result object to its own consumer.

This means you don't run the risk of forgetting to hide certain properties from your DTO. Even if the current metadata you need isn't confidential, it eventually might be, and you wouldn't want to risk a leak here.

Also, I really dislike manual labor, and manually removing properties from a JSON response sounds like cumbersome work that's prone to error and forgetfulness.

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.