Applicability of Single Responsibility Principle












20















I recently came by a seemingly trivial architectural problem. I had a simple repository in my code that was called like this (code is in C#):



var user = /* create user somehow */;
_userRepository.Add(user);
/* do some other stuff*/
_userRepository.SaveChanges();


SaveChanges was a simple wrapper that commits changes to database:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
}


Then, after some time, I needed to implement new logic that would send email notifications every time a user was created in the system. Since there were many calls to _userRepository.Add() and SaveChanges around the system, I decided to update SaveChanges like this:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
foreach (var newUser in dataContext.GetAddedUsers())
{
_eventService.RaiseEvent(new UserCreatedEvent(newUser ))
}
}


This way, external code could subscribe to UserCreatedEvent and handle the needed business logic that would send notifications.



But it was pointed out to me that my modification of SaveChanges violated the Single Responsibility principle, and that SaveChanges should just save and not fire any events.



Is this a valid point? It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function. And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.









share


















  • 10





    Your retort is: "OK, so how would you write it so that it doesn't violate SRP but still allows a single point of modification?"

    – Robert Harvey
    15 hours ago






  • 17





    My observation is that raising an event does not add an additional responsibility. In fact, quite the opposite: it delegates the responsibility somewhere else.

    – Robert Harvey
    15 hours ago











  • I think your coworker is right, but your question is valid and useful, so upvoted!

    – Andres F.
    13 hours ago






  • 5





    There's no such thing as a definitive definition of a Single Responsibility. The person pointing out that it violates SRP is correct using their personal definition and you are correct using your definition. I think your design is perfectly fine with the caveat that this event isn't a one-off whereby other similar functionality is done in different ways. Consistency is far, far, far more important to pay attention to than some vague guideline like SRP which carried to the extreme ends up with tons of very easy to understand classes that nobody knows how to make work in a system.

    – Dunk
    11 hours ago











  • Does SaveChanges update multiple users?

    – Nishant
    6 hours ago
















20















I recently came by a seemingly trivial architectural problem. I had a simple repository in my code that was called like this (code is in C#):



var user = /* create user somehow */;
_userRepository.Add(user);
/* do some other stuff*/
_userRepository.SaveChanges();


SaveChanges was a simple wrapper that commits changes to database:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
}


Then, after some time, I needed to implement new logic that would send email notifications every time a user was created in the system. Since there were many calls to _userRepository.Add() and SaveChanges around the system, I decided to update SaveChanges like this:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
foreach (var newUser in dataContext.GetAddedUsers())
{
_eventService.RaiseEvent(new UserCreatedEvent(newUser ))
}
}


This way, external code could subscribe to UserCreatedEvent and handle the needed business logic that would send notifications.



But it was pointed out to me that my modification of SaveChanges violated the Single Responsibility principle, and that SaveChanges should just save and not fire any events.



Is this a valid point? It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function. And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.









share


















  • 10





    Your retort is: "OK, so how would you write it so that it doesn't violate SRP but still allows a single point of modification?"

    – Robert Harvey
    15 hours ago






  • 17





    My observation is that raising an event does not add an additional responsibility. In fact, quite the opposite: it delegates the responsibility somewhere else.

    – Robert Harvey
    15 hours ago











  • I think your coworker is right, but your question is valid and useful, so upvoted!

    – Andres F.
    13 hours ago






  • 5





    There's no such thing as a definitive definition of a Single Responsibility. The person pointing out that it violates SRP is correct using their personal definition and you are correct using your definition. I think your design is perfectly fine with the caveat that this event isn't a one-off whereby other similar functionality is done in different ways. Consistency is far, far, far more important to pay attention to than some vague guideline like SRP which carried to the extreme ends up with tons of very easy to understand classes that nobody knows how to make work in a system.

    – Dunk
    11 hours ago











  • Does SaveChanges update multiple users?

    – Nishant
    6 hours ago














20












20








20


4






I recently came by a seemingly trivial architectural problem. I had a simple repository in my code that was called like this (code is in C#):



var user = /* create user somehow */;
_userRepository.Add(user);
/* do some other stuff*/
_userRepository.SaveChanges();


SaveChanges was a simple wrapper that commits changes to database:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
}


Then, after some time, I needed to implement new logic that would send email notifications every time a user was created in the system. Since there were many calls to _userRepository.Add() and SaveChanges around the system, I decided to update SaveChanges like this:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
foreach (var newUser in dataContext.GetAddedUsers())
{
_eventService.RaiseEvent(new UserCreatedEvent(newUser ))
}
}


This way, external code could subscribe to UserCreatedEvent and handle the needed business logic that would send notifications.



But it was pointed out to me that my modification of SaveChanges violated the Single Responsibility principle, and that SaveChanges should just save and not fire any events.



Is this a valid point? It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function. And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.









share














I recently came by a seemingly trivial architectural problem. I had a simple repository in my code that was called like this (code is in C#):



var user = /* create user somehow */;
_userRepository.Add(user);
/* do some other stuff*/
_userRepository.SaveChanges();


SaveChanges was a simple wrapper that commits changes to database:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
}


Then, after some time, I needed to implement new logic that would send email notifications every time a user was created in the system. Since there were many calls to _userRepository.Add() and SaveChanges around the system, I decided to update SaveChanges like this:



void SaveChanges()
{
_dataContext.SaveChanges();
_logger.Log("User DB updated: " + someImportantInfo);
foreach (var newUser in dataContext.GetAddedUsers())
{
_eventService.RaiseEvent(new UserCreatedEvent(newUser ))
}
}


This way, external code could subscribe to UserCreatedEvent and handle the needed business logic that would send notifications.



But it was pointed out to me that my modification of SaveChanges violated the Single Responsibility principle, and that SaveChanges should just save and not fire any events.



Is this a valid point? It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function. And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.







architecture single-responsibility





share












share










share



share










asked 16 hours ago









Andre BorgesAndre Borges

6941812




6941812








  • 10





    Your retort is: "OK, so how would you write it so that it doesn't violate SRP but still allows a single point of modification?"

    – Robert Harvey
    15 hours ago






  • 17





    My observation is that raising an event does not add an additional responsibility. In fact, quite the opposite: it delegates the responsibility somewhere else.

    – Robert Harvey
    15 hours ago











  • I think your coworker is right, but your question is valid and useful, so upvoted!

    – Andres F.
    13 hours ago






  • 5





    There's no such thing as a definitive definition of a Single Responsibility. The person pointing out that it violates SRP is correct using their personal definition and you are correct using your definition. I think your design is perfectly fine with the caveat that this event isn't a one-off whereby other similar functionality is done in different ways. Consistency is far, far, far more important to pay attention to than some vague guideline like SRP which carried to the extreme ends up with tons of very easy to understand classes that nobody knows how to make work in a system.

    – Dunk
    11 hours ago











  • Does SaveChanges update multiple users?

    – Nishant
    6 hours ago














  • 10





    Your retort is: "OK, so how would you write it so that it doesn't violate SRP but still allows a single point of modification?"

    – Robert Harvey
    15 hours ago






  • 17





    My observation is that raising an event does not add an additional responsibility. In fact, quite the opposite: it delegates the responsibility somewhere else.

    – Robert Harvey
    15 hours ago











  • I think your coworker is right, but your question is valid and useful, so upvoted!

    – Andres F.
    13 hours ago






  • 5





    There's no such thing as a definitive definition of a Single Responsibility. The person pointing out that it violates SRP is correct using their personal definition and you are correct using your definition. I think your design is perfectly fine with the caveat that this event isn't a one-off whereby other similar functionality is done in different ways. Consistency is far, far, far more important to pay attention to than some vague guideline like SRP which carried to the extreme ends up with tons of very easy to understand classes that nobody knows how to make work in a system.

    – Dunk
    11 hours ago











  • Does SaveChanges update multiple users?

    – Nishant
    6 hours ago








10




10





Your retort is: "OK, so how would you write it so that it doesn't violate SRP but still allows a single point of modification?"

– Robert Harvey
15 hours ago





Your retort is: "OK, so how would you write it so that it doesn't violate SRP but still allows a single point of modification?"

– Robert Harvey
15 hours ago




17




17





My observation is that raising an event does not add an additional responsibility. In fact, quite the opposite: it delegates the responsibility somewhere else.

– Robert Harvey
15 hours ago





My observation is that raising an event does not add an additional responsibility. In fact, quite the opposite: it delegates the responsibility somewhere else.

– Robert Harvey
15 hours ago













I think your coworker is right, but your question is valid and useful, so upvoted!

– Andres F.
13 hours ago





I think your coworker is right, but your question is valid and useful, so upvoted!

– Andres F.
13 hours ago




5




5





There's no such thing as a definitive definition of a Single Responsibility. The person pointing out that it violates SRP is correct using their personal definition and you are correct using your definition. I think your design is perfectly fine with the caveat that this event isn't a one-off whereby other similar functionality is done in different ways. Consistency is far, far, far more important to pay attention to than some vague guideline like SRP which carried to the extreme ends up with tons of very easy to understand classes that nobody knows how to make work in a system.

– Dunk
11 hours ago





There's no such thing as a definitive definition of a Single Responsibility. The person pointing out that it violates SRP is correct using their personal definition and you are correct using your definition. I think your design is perfectly fine with the caveat that this event isn't a one-off whereby other similar functionality is done in different ways. Consistency is far, far, far more important to pay attention to than some vague guideline like SRP which carried to the extreme ends up with tons of very easy to understand classes that nobody knows how to make work in a system.

– Dunk
11 hours ago













Does SaveChanges update multiple users?

– Nishant
6 hours ago





Does SaveChanges update multiple users?

– Nishant
6 hours ago










7 Answers
7






active

oldest

votes


















8














Sending a notification that the persistent data store changed seems like a sensible thing to do when saving.



Of course you shouldn't treat Add as a special case - you'd have to fire events for Modify and Delete as well. It's the special treatment of the "Add" case that smells, forces the reader to explain why it smells, and ultimately leads some readers of the code to conclude it must violate SRP.



A "notifying" repository that can be queried, changed, and fires events on changes, is a perfectly normal object. You can expect to find multiple variations thereof in pretty much any decently sized project.





But is a "notifying" repository actually what you need? You mentioned C#: Many people would agree that using a System.Collections.ObjectModel.ObservableCollection<> instead of System.Collections.Generic.List<> when the latter is all you need is all kinds of bad and wrong, but few would immediately point to SRP.



What you are doing now is swapping your UserList _userRepository with an ObservableUserCollection _userRepository. If that's the best course of action or not depends on the application. But while it unquestionably makes the _userRepository considerably less lightweight, in my humble opinion it doesn't violate SRP.






share|improve this answer

































    8














    Yes, it is a violation of the single responsibility principle and a valid point.



    A better design would be to have a separate process retrieve 'new users' from the repository and send the emails. Keeping track of which users have been sent an email, failures, resends, etc., etc.



    This way you can handle errors, crashes and the like as well as avoiding your repository grabbing every requirement which has the idea that events happen "when something is committed to the database".



    The repository doesn't know that a user you add is a new user. Its responsibility is simply storing the user.



    --Edit



    It's probably worth expanding on the comments below.




    The repository doesn't know that a user you add is a new user - yes it
    does, it has a method called Add. Its semantics implies that all added
    users are new users. Combine all the arguments passed to Add before
    calling Save - and you get all new users




    Incorrect. You are conflating "Added to the Repository" and "New".



    "Added to the Repository" means just what it says. I can add and remove and re-add users to various repositories.



    "New" is a state of a user defined by business rules.



    Currently the business rule might be "New == just been added to the repository", but that doesn't mean it's not a separate responsibility to know about and apply that rule.



    You have to be careful to avoid this kind of database-centric thinking. You will have edge case processes which add non-new users to the repository and when you send emails to them all the business will say "Of course those are not 'new' users! The actual rule is X"




    This answer is IMHO quite missing the point: the repo is exactly the
    one central place in the code which knows when new users are added




    Incorrect. For the reasons above, plus it's not a central location unless you actually include the email sending code in the class rather than just raising an event.



    You will have applications which use the repository class, but don't have the code to send the email. When you add users in those applications the email will not be sent.






    share|improve this answer





















    • 7





      The repository doesn't know that a user you add is a new user - yes it does, it has a method called Add. Its semantics implies that all added users are new users. Combine all the arguments passed to Add before calling Save - and you get all new users.

      – Andre Borges
      10 hours ago











    • I like this suggestion. However, pragmatism prevails over purity. Depending on the circumstances, adding an entirely new architectural layer to an existing application can be difficult to justify if all you need to do is literally send a single email when a user is added.

      – Alexander
      8 hours ago






    • 5





      This answer is IMHO quite missing the point: the repo is exactly the one central place in the code which knows when new users are added.

      – Doc Brown
      8 hours ago











    • But the event is not saying user added. It says user created. If we consider naming things properly and we agree with the semantical differences between add and create, then the event in the snippet is either wrong named or missplaced. I don't think the reviewer had anything against notyfing repositories. Probably It was concerned about the kind of event and its side effects.

      – Laiv
      2 hours ago






    • 1





      @Andre New to the repo, but not necessarily "new" in the business sense. its the conflation of these two ideas that's hiding the extra responsibility from first glance. I might import a tonne of old users to my new repository, or remove and re-add a user etc. There will be business rules about what a "new user" is beyond "has been added to the dB"

      – Ewan
      2 hours ago



















    6















    Is this a valid point?




    Yes it is, although it depends a lot on the structure of your code. I don't have the full context so I will try to talk in general.




    It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function.




    It absolutely isn't. Logging is not part of the business flow, it can be disabled, it shouldn't cause (business) side effects and should not influence the state and heath of your application in any way, even if you were for some reason not able to log anything anymore. Now compare that with the logic you added.




    And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.




    SRP works in tandem with ISP (S and I in SOLID). You end up with many classes and methods that do very specific things and nothing else. They are very focused, very easy to update or replace, and in general easy(er) to test. Of course in practice you'll also have a few bigger classes that deal with the orchestration: they will have a number of dependencies, and they will focus not on atomised actions, but on business actions, which may require multiple steps. As long as the business context is clear, they can too be called single responsibility, but as you correctly said, as the code grows, you may want to abstract some of it into new classes / interfaces.



    Now back to your particular example. If you absolutely must send a notification whenever a user is created and maybe even perform other more specialised actions, then you could create a separate service that encapsulates this requirement, something like UserCreationService, which exposes one method, Add(user), which handles both the storage (the call to your repository) and the notification as a single business action. Or do it in your original snippet, after _userRepository.SaveChanges();






    share|improve this answer



















    • 2





      Logging is not part of the business flow - how is this relevant in the context of SRP? If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting. What is the rule of a thumb for adding/not adding new logic to a function? "Will disabling it cause major business side effects?"

      – Andre Borges
      10 hours ago



















    2














    SRP is, theoretically, about people. The correct question is:



    Which "stakeholder" added the "send emails" requirement?



    If that stakeholder is also in charge of data persistence (unlikely but possible) then this does not violate SRP. Otherwise, it does.






    share|improve this answer

































      1














      Yes, it can be a valid requirement to have a repo which fires certain events on certain actions like Add or SaveChanges - and I am not going to question this (like some other answers), so let us assume this is requirement is perfectly justfied in the context of your system. And yes, encoding the event mechanics as well as the logging as well as the saving into one method violates the SRP (to a certain degree).



      The solution to this is to let your original repo stay to be responsible for committing changes to database, nothing else, and make a proxy repository which has exactly the same public interface, reuses the original repo and adds the additional event mechanics to the methods:



      // in EventFiringUserRepo:
      void SaveChanges()
      {
      _basicRepo.SaveChanges();
      FireEventsForNewlyAddedUsers();
      }

      void FireEventsForNewlyAddedUsers()
      {
      foreach (var newUser in _basicRepo.DataContext.GetAddedUsers())
      {
      _eventService.RaiseEvent(new UserCreatedEvent(newUser ))
      }
      }


      The new and the old repo class should both derive from a common interface, like shown in the classic Proxy pattern description.



      Then, in your original code, initialize _userRepository by an object of the new EventFiringUserRepo class. That way, you keep the original repo separated from the event mechanics. If required, you can have the event firing repo and the original repo side-by-side and let the callers decide if they use either the former or the latter.



      If this kind of separation is really worth it in the context of your system is something you and your reviewer have to decide by yourself. I probably would not separate the logging from the original code using the same approach, though it would be possible in priciple.





      share





















      • 1





        In addition to this answer. There are alternatives to proxies, like AOP.

        – Laiv
        1 hour ago













      • I think you miss the point, its not that raising an event breaks the SRP its that raising an event only for "New" users requires the repo to be responsible for knowing what constitutes a "New" user rather than a "Newly Added to Me" user

        – Ewan
        12 mins ago



















      1














      While technically there's nothing wrong with repositories notifying events, I would suggest looking at it from a functional point of view where its convenience rises some concerns.




      Creating users is one thing, its persistence a different one.




      Premise of mine



      Consider the previous premise before deciding whether the repository is the proper place to notify business events (regardless of the SRP). Note that I said business event because to me UserCreated has a different connotation than UserStored or UserAdded 1. I would also consider each of those events to be addressed to different audiences.



      On one side, creating users is a business-specific rule that might or might not involve persistence. It might involve more business operations, involving more database/network operations. Operations the persistence layer is unaware of. The persistence layer doesn't have enough context to decide whether the use case ended successfully or not.



      On the flip side, it's not necessarily true that _dataContext.SaveChanges(); has persisted the user successfully. It will depend on the database's transaction span. For instance, it could be true for databases like MongoDB, which transactions are atomic, but it could not, for traditional RDBMS implementing ACID transactions where there could be more transactions involved and yet to be committed.




      Is this a valid point?




      I would dare to say that, It's not only a matter of SRP (technically speaking), It's also a matter of convenience (functionally speaking). Is it convenient to cast business events from components that are not totally aware of the business operations in progress? Do they represent the right place as much as the right moment? Should the persistence orchestrate my business through notifications like this? Can we invalidate premature events?* 2




      It seems to me that the raising an event here is essentially the same
      thing as logging




      Absolutely not. Logging is meant to have no side effects, however, I suspect your event UserCreated is likely to cause other business operations to happen. Like notifications. 3




      it just says that such logic should be encapsulated in other classes,
      and it is OK for a repository to call these other classes




      Not necessarily true. SRP is not a class-specific concern only. It operates at different levels of abstractions, like layers, libraries and systems! It's about cohesion, about keeping together what changes for the same reasons. If the user creation (use case) changes it's likely the moment and the reasons for the event to happen also changes.





      1: Naming things adequately also matters.



      2: Say we sent UserCreated after _dataContext.SaveChanges();, but the whole database transaction failed later due to connection issues or constraints violations. Be careful with premature broadcasting of events, because its side effects can be hard to undo (if that is even possible).



      3: Bear in mind that a sent email cannot be undon






      share|improve this answer


























      • +1 Very good point about the transaction span. It can be premature to assert the user has been created, because rollbacks can happen; and unlike with a log, it's likely some other part of the app does something with the event.

        – Andres F.
        13 hours ago






      • 1





        Exactly. Events denote certainity. Something happened but it's over.

        – Laiv
        13 hours ago













      • @Laiv: Except when they don't. Microsoft has all sorts of events prefixed with Before or Preview that make no guarantees at all about certainty.

        – Robert Harvey
        12 hours ago











      • Hmm. I have never seen this sort of events. Would you mind sharing some references?

        – Laiv
        12 hours ago



















      0














      Currently SaveChanges does two things: it saves the changes and logs that it does so. Now you want to add another thing to it: send email notifications.



      You had the clever idea to add an event to it, but this was criticised for violating the Single Responsibility Principle (SRP), without noticing that it had already been violated.



      To get a pure SRP solution, first trigger the event, then call all the hooks for that event, of which there are now three: saving, logging, and finally sending emails.



      Either you trigger the event first, or you have to add to SaveChanges. Your solution is a hybrid between the two. It doesn't address the existing violation while it does encourage preventing it from increasing beyond three things. Refactoring the existing code to comply with SRP might require more work than is strictly necessary. It's up to your project how far they want to take SRP.






      share|improve this answer























        Your Answer








        StackExchange.ready(function() {
        var channelOptions = {
        tags: "".split(" "),
        id: "131"
        };
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function() {
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled) {
        StackExchange.using("snippets", function() {
        createEditor();
        });
        }
        else {
        createEditor();
        }
        });

        function createEditor() {
        StackExchange.prepareEditor({
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: false,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: null,
        bindNavPrevention: true,
        postfix: "",
        imageUploader: {
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        },
        onDemand: false,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        });


        }
        });














        draft saved

        draft discarded


















        StackExchange.ready(
        function () {
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f389237%2fapplicability-of-single-responsibility-principle%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown




















        StackExchange.ready(function () {
        $("#show-editor-button input, #show-editor-button button").click(function () {
        var showEditor = function() {
        $("#show-editor-button").hide();
        $("#post-form").removeClass("dno");
        StackExchange.editor.finallyInit();
        };

        var useFancy = $(this).data('confirm-use-fancy');
        if(useFancy == 'True') {
        var popupTitle = $(this).data('confirm-fancy-title');
        var popupBody = $(this).data('confirm-fancy-body');
        var popupAccept = $(this).data('confirm-fancy-accept-button');

        $(this).loadPopup({
        url: '/post/self-answer-popup',
        loaded: function(popup) {
        var pTitle = $(popup).find('h2');
        var pBody = $(popup).find('.popup-body');
        var pSubmit = $(popup).find('.popup-submit');

        pTitle.text(popupTitle);
        pBody.html(popupBody);
        pSubmit.val(popupAccept).click(showEditor);
        }
        })
        } else{
        var confirmText = $(this).data('confirm-text');
        if (confirmText ? confirm(confirmText) : true) {
        showEditor();
        }
        }
        });
        });






        7 Answers
        7






        active

        oldest

        votes








        7 Answers
        7






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        8














        Sending a notification that the persistent data store changed seems like a sensible thing to do when saving.



        Of course you shouldn't treat Add as a special case - you'd have to fire events for Modify and Delete as well. It's the special treatment of the "Add" case that smells, forces the reader to explain why it smells, and ultimately leads some readers of the code to conclude it must violate SRP.



        A "notifying" repository that can be queried, changed, and fires events on changes, is a perfectly normal object. You can expect to find multiple variations thereof in pretty much any decently sized project.





        But is a "notifying" repository actually what you need? You mentioned C#: Many people would agree that using a System.Collections.ObjectModel.ObservableCollection<> instead of System.Collections.Generic.List<> when the latter is all you need is all kinds of bad and wrong, but few would immediately point to SRP.



        What you are doing now is swapping your UserList _userRepository with an ObservableUserCollection _userRepository. If that's the best course of action or not depends on the application. But while it unquestionably makes the _userRepository considerably less lightweight, in my humble opinion it doesn't violate SRP.






        share|improve this answer






























          8














          Sending a notification that the persistent data store changed seems like a sensible thing to do when saving.



          Of course you shouldn't treat Add as a special case - you'd have to fire events for Modify and Delete as well. It's the special treatment of the "Add" case that smells, forces the reader to explain why it smells, and ultimately leads some readers of the code to conclude it must violate SRP.



          A "notifying" repository that can be queried, changed, and fires events on changes, is a perfectly normal object. You can expect to find multiple variations thereof in pretty much any decently sized project.





          But is a "notifying" repository actually what you need? You mentioned C#: Many people would agree that using a System.Collections.ObjectModel.ObservableCollection<> instead of System.Collections.Generic.List<> when the latter is all you need is all kinds of bad and wrong, but few would immediately point to SRP.



          What you are doing now is swapping your UserList _userRepository with an ObservableUserCollection _userRepository. If that's the best course of action or not depends on the application. But while it unquestionably makes the _userRepository considerably less lightweight, in my humble opinion it doesn't violate SRP.






          share|improve this answer




























            8












            8








            8







            Sending a notification that the persistent data store changed seems like a sensible thing to do when saving.



            Of course you shouldn't treat Add as a special case - you'd have to fire events for Modify and Delete as well. It's the special treatment of the "Add" case that smells, forces the reader to explain why it smells, and ultimately leads some readers of the code to conclude it must violate SRP.



            A "notifying" repository that can be queried, changed, and fires events on changes, is a perfectly normal object. You can expect to find multiple variations thereof in pretty much any decently sized project.





            But is a "notifying" repository actually what you need? You mentioned C#: Many people would agree that using a System.Collections.ObjectModel.ObservableCollection<> instead of System.Collections.Generic.List<> when the latter is all you need is all kinds of bad and wrong, but few would immediately point to SRP.



            What you are doing now is swapping your UserList _userRepository with an ObservableUserCollection _userRepository. If that's the best course of action or not depends on the application. But while it unquestionably makes the _userRepository considerably less lightweight, in my humble opinion it doesn't violate SRP.






            share|improve this answer















            Sending a notification that the persistent data store changed seems like a sensible thing to do when saving.



            Of course you shouldn't treat Add as a special case - you'd have to fire events for Modify and Delete as well. It's the special treatment of the "Add" case that smells, forces the reader to explain why it smells, and ultimately leads some readers of the code to conclude it must violate SRP.



            A "notifying" repository that can be queried, changed, and fires events on changes, is a perfectly normal object. You can expect to find multiple variations thereof in pretty much any decently sized project.





            But is a "notifying" repository actually what you need? You mentioned C#: Many people would agree that using a System.Collections.ObjectModel.ObservableCollection<> instead of System.Collections.Generic.List<> when the latter is all you need is all kinds of bad and wrong, but few would immediately point to SRP.



            What you are doing now is swapping your UserList _userRepository with an ObservableUserCollection _userRepository. If that's the best course of action or not depends on the application. But while it unquestionably makes the _userRepository considerably less lightweight, in my humble opinion it doesn't violate SRP.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited 7 hours ago

























            answered 10 hours ago









            PeterPeter

            2,937515




            2,937515

























                8














                Yes, it is a violation of the single responsibility principle and a valid point.



                A better design would be to have a separate process retrieve 'new users' from the repository and send the emails. Keeping track of which users have been sent an email, failures, resends, etc., etc.



                This way you can handle errors, crashes and the like as well as avoiding your repository grabbing every requirement which has the idea that events happen "when something is committed to the database".



                The repository doesn't know that a user you add is a new user. Its responsibility is simply storing the user.



                --Edit



                It's probably worth expanding on the comments below.




                The repository doesn't know that a user you add is a new user - yes it
                does, it has a method called Add. Its semantics implies that all added
                users are new users. Combine all the arguments passed to Add before
                calling Save - and you get all new users




                Incorrect. You are conflating "Added to the Repository" and "New".



                "Added to the Repository" means just what it says. I can add and remove and re-add users to various repositories.



                "New" is a state of a user defined by business rules.



                Currently the business rule might be "New == just been added to the repository", but that doesn't mean it's not a separate responsibility to know about and apply that rule.



                You have to be careful to avoid this kind of database-centric thinking. You will have edge case processes which add non-new users to the repository and when you send emails to them all the business will say "Of course those are not 'new' users! The actual rule is X"




                This answer is IMHO quite missing the point: the repo is exactly the
                one central place in the code which knows when new users are added




                Incorrect. For the reasons above, plus it's not a central location unless you actually include the email sending code in the class rather than just raising an event.



                You will have applications which use the repository class, but don't have the code to send the email. When you add users in those applications the email will not be sent.






                share|improve this answer





















                • 7





                  The repository doesn't know that a user you add is a new user - yes it does, it has a method called Add. Its semantics implies that all added users are new users. Combine all the arguments passed to Add before calling Save - and you get all new users.

                  – Andre Borges
                  10 hours ago











                • I like this suggestion. However, pragmatism prevails over purity. Depending on the circumstances, adding an entirely new architectural layer to an existing application can be difficult to justify if all you need to do is literally send a single email when a user is added.

                  – Alexander
                  8 hours ago






                • 5





                  This answer is IMHO quite missing the point: the repo is exactly the one central place in the code which knows when new users are added.

                  – Doc Brown
                  8 hours ago











                • But the event is not saying user added. It says user created. If we consider naming things properly and we agree with the semantical differences between add and create, then the event in the snippet is either wrong named or missplaced. I don't think the reviewer had anything against notyfing repositories. Probably It was concerned about the kind of event and its side effects.

                  – Laiv
                  2 hours ago






                • 1





                  @Andre New to the repo, but not necessarily "new" in the business sense. its the conflation of these two ideas that's hiding the extra responsibility from first glance. I might import a tonne of old users to my new repository, or remove and re-add a user etc. There will be business rules about what a "new user" is beyond "has been added to the dB"

                  – Ewan
                  2 hours ago
















                8














                Yes, it is a violation of the single responsibility principle and a valid point.



                A better design would be to have a separate process retrieve 'new users' from the repository and send the emails. Keeping track of which users have been sent an email, failures, resends, etc., etc.



                This way you can handle errors, crashes and the like as well as avoiding your repository grabbing every requirement which has the idea that events happen "when something is committed to the database".



                The repository doesn't know that a user you add is a new user. Its responsibility is simply storing the user.



                --Edit



                It's probably worth expanding on the comments below.




                The repository doesn't know that a user you add is a new user - yes it
                does, it has a method called Add. Its semantics implies that all added
                users are new users. Combine all the arguments passed to Add before
                calling Save - and you get all new users




                Incorrect. You are conflating "Added to the Repository" and "New".



                "Added to the Repository" means just what it says. I can add and remove and re-add users to various repositories.



                "New" is a state of a user defined by business rules.



                Currently the business rule might be "New == just been added to the repository", but that doesn't mean it's not a separate responsibility to know about and apply that rule.



                You have to be careful to avoid this kind of database-centric thinking. You will have edge case processes which add non-new users to the repository and when you send emails to them all the business will say "Of course those are not 'new' users! The actual rule is X"




                This answer is IMHO quite missing the point: the repo is exactly the
                one central place in the code which knows when new users are added




                Incorrect. For the reasons above, plus it's not a central location unless you actually include the email sending code in the class rather than just raising an event.



                You will have applications which use the repository class, but don't have the code to send the email. When you add users in those applications the email will not be sent.






                share|improve this answer





















                • 7





                  The repository doesn't know that a user you add is a new user - yes it does, it has a method called Add. Its semantics implies that all added users are new users. Combine all the arguments passed to Add before calling Save - and you get all new users.

                  – Andre Borges
                  10 hours ago











                • I like this suggestion. However, pragmatism prevails over purity. Depending on the circumstances, adding an entirely new architectural layer to an existing application can be difficult to justify if all you need to do is literally send a single email when a user is added.

                  – Alexander
                  8 hours ago






                • 5





                  This answer is IMHO quite missing the point: the repo is exactly the one central place in the code which knows when new users are added.

                  – Doc Brown
                  8 hours ago











                • But the event is not saying user added. It says user created. If we consider naming things properly and we agree with the semantical differences between add and create, then the event in the snippet is either wrong named or missplaced. I don't think the reviewer had anything against notyfing repositories. Probably It was concerned about the kind of event and its side effects.

                  – Laiv
                  2 hours ago






                • 1





                  @Andre New to the repo, but not necessarily "new" in the business sense. its the conflation of these two ideas that's hiding the extra responsibility from first glance. I might import a tonne of old users to my new repository, or remove and re-add a user etc. There will be business rules about what a "new user" is beyond "has been added to the dB"

                  – Ewan
                  2 hours ago














                8












                8








                8







                Yes, it is a violation of the single responsibility principle and a valid point.



                A better design would be to have a separate process retrieve 'new users' from the repository and send the emails. Keeping track of which users have been sent an email, failures, resends, etc., etc.



                This way you can handle errors, crashes and the like as well as avoiding your repository grabbing every requirement which has the idea that events happen "when something is committed to the database".



                The repository doesn't know that a user you add is a new user. Its responsibility is simply storing the user.



                --Edit



                It's probably worth expanding on the comments below.




                The repository doesn't know that a user you add is a new user - yes it
                does, it has a method called Add. Its semantics implies that all added
                users are new users. Combine all the arguments passed to Add before
                calling Save - and you get all new users




                Incorrect. You are conflating "Added to the Repository" and "New".



                "Added to the Repository" means just what it says. I can add and remove and re-add users to various repositories.



                "New" is a state of a user defined by business rules.



                Currently the business rule might be "New == just been added to the repository", but that doesn't mean it's not a separate responsibility to know about and apply that rule.



                You have to be careful to avoid this kind of database-centric thinking. You will have edge case processes which add non-new users to the repository and when you send emails to them all the business will say "Of course those are not 'new' users! The actual rule is X"




                This answer is IMHO quite missing the point: the repo is exactly the
                one central place in the code which knows when new users are added




                Incorrect. For the reasons above, plus it's not a central location unless you actually include the email sending code in the class rather than just raising an event.



                You will have applications which use the repository class, but don't have the code to send the email. When you add users in those applications the email will not be sent.






                share|improve this answer















                Yes, it is a violation of the single responsibility principle and a valid point.



                A better design would be to have a separate process retrieve 'new users' from the repository and send the emails. Keeping track of which users have been sent an email, failures, resends, etc., etc.



                This way you can handle errors, crashes and the like as well as avoiding your repository grabbing every requirement which has the idea that events happen "when something is committed to the database".



                The repository doesn't know that a user you add is a new user. Its responsibility is simply storing the user.



                --Edit



                It's probably worth expanding on the comments below.




                The repository doesn't know that a user you add is a new user - yes it
                does, it has a method called Add. Its semantics implies that all added
                users are new users. Combine all the arguments passed to Add before
                calling Save - and you get all new users




                Incorrect. You are conflating "Added to the Repository" and "New".



                "Added to the Repository" means just what it says. I can add and remove and re-add users to various repositories.



                "New" is a state of a user defined by business rules.



                Currently the business rule might be "New == just been added to the repository", but that doesn't mean it's not a separate responsibility to know about and apply that rule.



                You have to be careful to avoid this kind of database-centric thinking. You will have edge case processes which add non-new users to the repository and when you send emails to them all the business will say "Of course those are not 'new' users! The actual rule is X"




                This answer is IMHO quite missing the point: the repo is exactly the
                one central place in the code which knows when new users are added




                Incorrect. For the reasons above, plus it's not a central location unless you actually include the email sending code in the class rather than just raising an event.



                You will have applications which use the repository class, but don't have the code to send the email. When you add users in those applications the email will not be sent.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 8 mins ago

























                answered 15 hours ago









                EwanEwan

                42.3k33593




                42.3k33593








                • 7





                  The repository doesn't know that a user you add is a new user - yes it does, it has a method called Add. Its semantics implies that all added users are new users. Combine all the arguments passed to Add before calling Save - and you get all new users.

                  – Andre Borges
                  10 hours ago











                • I like this suggestion. However, pragmatism prevails over purity. Depending on the circumstances, adding an entirely new architectural layer to an existing application can be difficult to justify if all you need to do is literally send a single email when a user is added.

                  – Alexander
                  8 hours ago






                • 5





                  This answer is IMHO quite missing the point: the repo is exactly the one central place in the code which knows when new users are added.

                  – Doc Brown
                  8 hours ago











                • But the event is not saying user added. It says user created. If we consider naming things properly and we agree with the semantical differences between add and create, then the event in the snippet is either wrong named or missplaced. I don't think the reviewer had anything against notyfing repositories. Probably It was concerned about the kind of event and its side effects.

                  – Laiv
                  2 hours ago






                • 1





                  @Andre New to the repo, but not necessarily "new" in the business sense. its the conflation of these two ideas that's hiding the extra responsibility from first glance. I might import a tonne of old users to my new repository, or remove and re-add a user etc. There will be business rules about what a "new user" is beyond "has been added to the dB"

                  – Ewan
                  2 hours ago














                • 7





                  The repository doesn't know that a user you add is a new user - yes it does, it has a method called Add. Its semantics implies that all added users are new users. Combine all the arguments passed to Add before calling Save - and you get all new users.

                  – Andre Borges
                  10 hours ago











                • I like this suggestion. However, pragmatism prevails over purity. Depending on the circumstances, adding an entirely new architectural layer to an existing application can be difficult to justify if all you need to do is literally send a single email when a user is added.

                  – Alexander
                  8 hours ago






                • 5





                  This answer is IMHO quite missing the point: the repo is exactly the one central place in the code which knows when new users are added.

                  – Doc Brown
                  8 hours ago











                • But the event is not saying user added. It says user created. If we consider naming things properly and we agree with the semantical differences between add and create, then the event in the snippet is either wrong named or missplaced. I don't think the reviewer had anything against notyfing repositories. Probably It was concerned about the kind of event and its side effects.

                  – Laiv
                  2 hours ago






                • 1





                  @Andre New to the repo, but not necessarily "new" in the business sense. its the conflation of these two ideas that's hiding the extra responsibility from first glance. I might import a tonne of old users to my new repository, or remove and re-add a user etc. There will be business rules about what a "new user" is beyond "has been added to the dB"

                  – Ewan
                  2 hours ago








                7




                7





                The repository doesn't know that a user you add is a new user - yes it does, it has a method called Add. Its semantics implies that all added users are new users. Combine all the arguments passed to Add before calling Save - and you get all new users.

                – Andre Borges
                10 hours ago





                The repository doesn't know that a user you add is a new user - yes it does, it has a method called Add. Its semantics implies that all added users are new users. Combine all the arguments passed to Add before calling Save - and you get all new users.

                – Andre Borges
                10 hours ago













                I like this suggestion. However, pragmatism prevails over purity. Depending on the circumstances, adding an entirely new architectural layer to an existing application can be difficult to justify if all you need to do is literally send a single email when a user is added.

                – Alexander
                8 hours ago





                I like this suggestion. However, pragmatism prevails over purity. Depending on the circumstances, adding an entirely new architectural layer to an existing application can be difficult to justify if all you need to do is literally send a single email when a user is added.

                – Alexander
                8 hours ago




                5




                5





                This answer is IMHO quite missing the point: the repo is exactly the one central place in the code which knows when new users are added.

                – Doc Brown
                8 hours ago





                This answer is IMHO quite missing the point: the repo is exactly the one central place in the code which knows when new users are added.

                – Doc Brown
                8 hours ago













                But the event is not saying user added. It says user created. If we consider naming things properly and we agree with the semantical differences between add and create, then the event in the snippet is either wrong named or missplaced. I don't think the reviewer had anything against notyfing repositories. Probably It was concerned about the kind of event and its side effects.

                – Laiv
                2 hours ago





                But the event is not saying user added. It says user created. If we consider naming things properly and we agree with the semantical differences between add and create, then the event in the snippet is either wrong named or missplaced. I don't think the reviewer had anything against notyfing repositories. Probably It was concerned about the kind of event and its side effects.

                – Laiv
                2 hours ago




                1




                1





                @Andre New to the repo, but not necessarily "new" in the business sense. its the conflation of these two ideas that's hiding the extra responsibility from first glance. I might import a tonne of old users to my new repository, or remove and re-add a user etc. There will be business rules about what a "new user" is beyond "has been added to the dB"

                – Ewan
                2 hours ago





                @Andre New to the repo, but not necessarily "new" in the business sense. its the conflation of these two ideas that's hiding the extra responsibility from first glance. I might import a tonne of old users to my new repository, or remove and re-add a user etc. There will be business rules about what a "new user" is beyond "has been added to the dB"

                – Ewan
                2 hours ago











                6















                Is this a valid point?




                Yes it is, although it depends a lot on the structure of your code. I don't have the full context so I will try to talk in general.




                It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function.




                It absolutely isn't. Logging is not part of the business flow, it can be disabled, it shouldn't cause (business) side effects and should not influence the state and heath of your application in any way, even if you were for some reason not able to log anything anymore. Now compare that with the logic you added.




                And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.




                SRP works in tandem with ISP (S and I in SOLID). You end up with many classes and methods that do very specific things and nothing else. They are very focused, very easy to update or replace, and in general easy(er) to test. Of course in practice you'll also have a few bigger classes that deal with the orchestration: they will have a number of dependencies, and they will focus not on atomised actions, but on business actions, which may require multiple steps. As long as the business context is clear, they can too be called single responsibility, but as you correctly said, as the code grows, you may want to abstract some of it into new classes / interfaces.



                Now back to your particular example. If you absolutely must send a notification whenever a user is created and maybe even perform other more specialised actions, then you could create a separate service that encapsulates this requirement, something like UserCreationService, which exposes one method, Add(user), which handles both the storage (the call to your repository) and the notification as a single business action. Or do it in your original snippet, after _userRepository.SaveChanges();






                share|improve this answer



















                • 2





                  Logging is not part of the business flow - how is this relevant in the context of SRP? If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting. What is the rule of a thumb for adding/not adding new logic to a function? "Will disabling it cause major business side effects?"

                  – Andre Borges
                  10 hours ago
















                6















                Is this a valid point?




                Yes it is, although it depends a lot on the structure of your code. I don't have the full context so I will try to talk in general.




                It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function.




                It absolutely isn't. Logging is not part of the business flow, it can be disabled, it shouldn't cause (business) side effects and should not influence the state and heath of your application in any way, even if you were for some reason not able to log anything anymore. Now compare that with the logic you added.




                And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.




                SRP works in tandem with ISP (S and I in SOLID). You end up with many classes and methods that do very specific things and nothing else. They are very focused, very easy to update or replace, and in general easy(er) to test. Of course in practice you'll also have a few bigger classes that deal with the orchestration: they will have a number of dependencies, and they will focus not on atomised actions, but on business actions, which may require multiple steps. As long as the business context is clear, they can too be called single responsibility, but as you correctly said, as the code grows, you may want to abstract some of it into new classes / interfaces.



                Now back to your particular example. If you absolutely must send a notification whenever a user is created and maybe even perform other more specialised actions, then you could create a separate service that encapsulates this requirement, something like UserCreationService, which exposes one method, Add(user), which handles both the storage (the call to your repository) and the notification as a single business action. Or do it in your original snippet, after _userRepository.SaveChanges();






                share|improve this answer



















                • 2





                  Logging is not part of the business flow - how is this relevant in the context of SRP? If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting. What is the rule of a thumb for adding/not adding new logic to a function? "Will disabling it cause major business side effects?"

                  – Andre Borges
                  10 hours ago














                6












                6








                6








                Is this a valid point?




                Yes it is, although it depends a lot on the structure of your code. I don't have the full context so I will try to talk in general.




                It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function.




                It absolutely isn't. Logging is not part of the business flow, it can be disabled, it shouldn't cause (business) side effects and should not influence the state and heath of your application in any way, even if you were for some reason not able to log anything anymore. Now compare that with the logic you added.




                And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.




                SRP works in tandem with ISP (S and I in SOLID). You end up with many classes and methods that do very specific things and nothing else. They are very focused, very easy to update or replace, and in general easy(er) to test. Of course in practice you'll also have a few bigger classes that deal with the orchestration: they will have a number of dependencies, and they will focus not on atomised actions, but on business actions, which may require multiple steps. As long as the business context is clear, they can too be called single responsibility, but as you correctly said, as the code grows, you may want to abstract some of it into new classes / interfaces.



                Now back to your particular example. If you absolutely must send a notification whenever a user is created and maybe even perform other more specialised actions, then you could create a separate service that encapsulates this requirement, something like UserCreationService, which exposes one method, Add(user), which handles both the storage (the call to your repository) and the notification as a single business action. Or do it in your original snippet, after _userRepository.SaveChanges();






                share|improve this answer














                Is this a valid point?




                Yes it is, although it depends a lot on the structure of your code. I don't have the full context so I will try to talk in general.




                It seems to me that the raising an event here is essentially the same thing as logging: just adding some side functionality to the function.




                It absolutely isn't. Logging is not part of the business flow, it can be disabled, it shouldn't cause (business) side effects and should not influence the state and heath of your application in any way, even if you were for some reason not able to log anything anymore. Now compare that with the logic you added.




                And SRP does not prohibit you from using logging or firing events in your functions, it just says that such logic should be encapsulated in other classes, and it is OK for a repository to call these other classes.




                SRP works in tandem with ISP (S and I in SOLID). You end up with many classes and methods that do very specific things and nothing else. They are very focused, very easy to update or replace, and in general easy(er) to test. Of course in practice you'll also have a few bigger classes that deal with the orchestration: they will have a number of dependencies, and they will focus not on atomised actions, but on business actions, which may require multiple steps. As long as the business context is clear, they can too be called single responsibility, but as you correctly said, as the code grows, you may want to abstract some of it into new classes / interfaces.



                Now back to your particular example. If you absolutely must send a notification whenever a user is created and maybe even perform other more specialised actions, then you could create a separate service that encapsulates this requirement, something like UserCreationService, which exposes one method, Add(user), which handles both the storage (the call to your repository) and the notification as a single business action. Or do it in your original snippet, after _userRepository.SaveChanges();







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 12 hours ago









                asyncasync

                58259




                58259








                • 2





                  Logging is not part of the business flow - how is this relevant in the context of SRP? If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting. What is the rule of a thumb for adding/not adding new logic to a function? "Will disabling it cause major business side effects?"

                  – Andre Borges
                  10 hours ago














                • 2





                  Logging is not part of the business flow - how is this relevant in the context of SRP? If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting. What is the rule of a thumb for adding/not adding new logic to a function? "Will disabling it cause major business side effects?"

                  – Andre Borges
                  10 hours ago








                2




                2





                Logging is not part of the business flow - how is this relevant in the context of SRP? If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting. What is the rule of a thumb for adding/not adding new logic to a function? "Will disabling it cause major business side effects?"

                – Andre Borges
                10 hours ago





                Logging is not part of the business flow - how is this relevant in the context of SRP? If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting. What is the rule of a thumb for adding/not adding new logic to a function? "Will disabling it cause major business side effects?"

                – Andre Borges
                10 hours ago











                2














                SRP is, theoretically, about people. The correct question is:



                Which "stakeholder" added the "send emails" requirement?



                If that stakeholder is also in charge of data persistence (unlikely but possible) then this does not violate SRP. Otherwise, it does.






                share|improve this answer






























                  2














                  SRP is, theoretically, about people. The correct question is:



                  Which "stakeholder" added the "send emails" requirement?



                  If that stakeholder is also in charge of data persistence (unlikely but possible) then this does not violate SRP. Otherwise, it does.






                  share|improve this answer




























                    2












                    2








                    2







                    SRP is, theoretically, about people. The correct question is:



                    Which "stakeholder" added the "send emails" requirement?



                    If that stakeholder is also in charge of data persistence (unlikely but possible) then this does not violate SRP. Otherwise, it does.






                    share|improve this answer















                    SRP is, theoretically, about people. The correct question is:



                    Which "stakeholder" added the "send emails" requirement?



                    If that stakeholder is also in charge of data persistence (unlikely but possible) then this does not violate SRP. Otherwise, it does.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited 12 hours ago

























                    answered 12 hours ago









                    user949300user949300

                    5,85511528




                    5,85511528























                        1














                        Yes, it can be a valid requirement to have a repo which fires certain events on certain actions like Add or SaveChanges - and I am not going to question this (like some other answers), so let us assume this is requirement is perfectly justfied in the context of your system. And yes, encoding the event mechanics as well as the logging as well as the saving into one method violates the SRP (to a certain degree).



                        The solution to this is to let your original repo stay to be responsible for committing changes to database, nothing else, and make a proxy repository which has exactly the same public interface, reuses the original repo and adds the additional event mechanics to the methods:



                        // in EventFiringUserRepo:
                        void SaveChanges()
                        {
                        _basicRepo.SaveChanges();
                        FireEventsForNewlyAddedUsers();
                        }

                        void FireEventsForNewlyAddedUsers()
                        {
                        foreach (var newUser in _basicRepo.DataContext.GetAddedUsers())
                        {
                        _eventService.RaiseEvent(new UserCreatedEvent(newUser ))
                        }
                        }


                        The new and the old repo class should both derive from a common interface, like shown in the classic Proxy pattern description.



                        Then, in your original code, initialize _userRepository by an object of the new EventFiringUserRepo class. That way, you keep the original repo separated from the event mechanics. If required, you can have the event firing repo and the original repo side-by-side and let the callers decide if they use either the former or the latter.



                        If this kind of separation is really worth it in the context of your system is something you and your reviewer have to decide by yourself. I probably would not separate the logging from the original code using the same approach, though it would be possible in priciple.





                        share





















                        • 1





                          In addition to this answer. There are alternatives to proxies, like AOP.

                          – Laiv
                          1 hour ago













                        • I think you miss the point, its not that raising an event breaks the SRP its that raising an event only for "New" users requires the repo to be responsible for knowing what constitutes a "New" user rather than a "Newly Added to Me" user

                          – Ewan
                          12 mins ago
















                        1














                        Yes, it can be a valid requirement to have a repo which fires certain events on certain actions like Add or SaveChanges - and I am not going to question this (like some other answers), so let us assume this is requirement is perfectly justfied in the context of your system. And yes, encoding the event mechanics as well as the logging as well as the saving into one method violates the SRP (to a certain degree).



                        The solution to this is to let your original repo stay to be responsible for committing changes to database, nothing else, and make a proxy repository which has exactly the same public interface, reuses the original repo and adds the additional event mechanics to the methods:



                        // in EventFiringUserRepo:
                        void SaveChanges()
                        {
                        _basicRepo.SaveChanges();
                        FireEventsForNewlyAddedUsers();
                        }

                        void FireEventsForNewlyAddedUsers()
                        {
                        foreach (var newUser in _basicRepo.DataContext.GetAddedUsers())
                        {
                        _eventService.RaiseEvent(new UserCreatedEvent(newUser ))
                        }
                        }


                        The new and the old repo class should both derive from a common interface, like shown in the classic Proxy pattern description.



                        Then, in your original code, initialize _userRepository by an object of the new EventFiringUserRepo class. That way, you keep the original repo separated from the event mechanics. If required, you can have the event firing repo and the original repo side-by-side and let the callers decide if they use either the former or the latter.



                        If this kind of separation is really worth it in the context of your system is something you and your reviewer have to decide by yourself. I probably would not separate the logging from the original code using the same approach, though it would be possible in priciple.





                        share





















                        • 1





                          In addition to this answer. There are alternatives to proxies, like AOP.

                          – Laiv
                          1 hour ago













                        • I think you miss the point, its not that raising an event breaks the SRP its that raising an event only for "New" users requires the repo to be responsible for knowing what constitutes a "New" user rather than a "Newly Added to Me" user

                          – Ewan
                          12 mins ago














                        1












                        1








                        1







                        Yes, it can be a valid requirement to have a repo which fires certain events on certain actions like Add or SaveChanges - and I am not going to question this (like some other answers), so let us assume this is requirement is perfectly justfied in the context of your system. And yes, encoding the event mechanics as well as the logging as well as the saving into one method violates the SRP (to a certain degree).



                        The solution to this is to let your original repo stay to be responsible for committing changes to database, nothing else, and make a proxy repository which has exactly the same public interface, reuses the original repo and adds the additional event mechanics to the methods:



                        // in EventFiringUserRepo:
                        void SaveChanges()
                        {
                        _basicRepo.SaveChanges();
                        FireEventsForNewlyAddedUsers();
                        }

                        void FireEventsForNewlyAddedUsers()
                        {
                        foreach (var newUser in _basicRepo.DataContext.GetAddedUsers())
                        {
                        _eventService.RaiseEvent(new UserCreatedEvent(newUser ))
                        }
                        }


                        The new and the old repo class should both derive from a common interface, like shown in the classic Proxy pattern description.



                        Then, in your original code, initialize _userRepository by an object of the new EventFiringUserRepo class. That way, you keep the original repo separated from the event mechanics. If required, you can have the event firing repo and the original repo side-by-side and let the callers decide if they use either the former or the latter.



                        If this kind of separation is really worth it in the context of your system is something you and your reviewer have to decide by yourself. I probably would not separate the logging from the original code using the same approach, though it would be possible in priciple.





                        share















                        Yes, it can be a valid requirement to have a repo which fires certain events on certain actions like Add or SaveChanges - and I am not going to question this (like some other answers), so let us assume this is requirement is perfectly justfied in the context of your system. And yes, encoding the event mechanics as well as the logging as well as the saving into one method violates the SRP (to a certain degree).



                        The solution to this is to let your original repo stay to be responsible for committing changes to database, nothing else, and make a proxy repository which has exactly the same public interface, reuses the original repo and adds the additional event mechanics to the methods:



                        // in EventFiringUserRepo:
                        void SaveChanges()
                        {
                        _basicRepo.SaveChanges();
                        FireEventsForNewlyAddedUsers();
                        }

                        void FireEventsForNewlyAddedUsers()
                        {
                        foreach (var newUser in _basicRepo.DataContext.GetAddedUsers())
                        {
                        _eventService.RaiseEvent(new UserCreatedEvent(newUser ))
                        }
                        }


                        The new and the old repo class should both derive from a common interface, like shown in the classic Proxy pattern description.



                        Then, in your original code, initialize _userRepository by an object of the new EventFiringUserRepo class. That way, you keep the original repo separated from the event mechanics. If required, you can have the event firing repo and the original repo side-by-side and let the callers decide if they use either the former or the latter.



                        If this kind of separation is really worth it in the context of your system is something you and your reviewer have to decide by yourself. I probably would not separate the logging from the original code using the same approach, though it would be possible in priciple.






                        share













                        share


                        share








                        edited 7 hours ago

























                        answered 7 hours ago









                        Doc BrownDoc Brown

                        136k23251403




                        136k23251403








                        • 1





                          In addition to this answer. There are alternatives to proxies, like AOP.

                          – Laiv
                          1 hour ago













                        • I think you miss the point, its not that raising an event breaks the SRP its that raising an event only for "New" users requires the repo to be responsible for knowing what constitutes a "New" user rather than a "Newly Added to Me" user

                          – Ewan
                          12 mins ago














                        • 1





                          In addition to this answer. There are alternatives to proxies, like AOP.

                          – Laiv
                          1 hour ago













                        • I think you miss the point, its not that raising an event breaks the SRP its that raising an event only for "New" users requires the repo to be responsible for knowing what constitutes a "New" user rather than a "Newly Added to Me" user

                          – Ewan
                          12 mins ago








                        1




                        1





                        In addition to this answer. There are alternatives to proxies, like AOP.

                        – Laiv
                        1 hour ago







                        In addition to this answer. There are alternatives to proxies, like AOP.

                        – Laiv
                        1 hour ago















                        I think you miss the point, its not that raising an event breaks the SRP its that raising an event only for "New" users requires the repo to be responsible for knowing what constitutes a "New" user rather than a "Newly Added to Me" user

                        – Ewan
                        12 mins ago





                        I think you miss the point, its not that raising an event breaks the SRP its that raising an event only for "New" users requires the repo to be responsible for knowing what constitutes a "New" user rather than a "Newly Added to Me" user

                        – Ewan
                        12 mins ago











                        1














                        While technically there's nothing wrong with repositories notifying events, I would suggest looking at it from a functional point of view where its convenience rises some concerns.




                        Creating users is one thing, its persistence a different one.




                        Premise of mine



                        Consider the previous premise before deciding whether the repository is the proper place to notify business events (regardless of the SRP). Note that I said business event because to me UserCreated has a different connotation than UserStored or UserAdded 1. I would also consider each of those events to be addressed to different audiences.



                        On one side, creating users is a business-specific rule that might or might not involve persistence. It might involve more business operations, involving more database/network operations. Operations the persistence layer is unaware of. The persistence layer doesn't have enough context to decide whether the use case ended successfully or not.



                        On the flip side, it's not necessarily true that _dataContext.SaveChanges(); has persisted the user successfully. It will depend on the database's transaction span. For instance, it could be true for databases like MongoDB, which transactions are atomic, but it could not, for traditional RDBMS implementing ACID transactions where there could be more transactions involved and yet to be committed.




                        Is this a valid point?




                        I would dare to say that, It's not only a matter of SRP (technically speaking), It's also a matter of convenience (functionally speaking). Is it convenient to cast business events from components that are not totally aware of the business operations in progress? Do they represent the right place as much as the right moment? Should the persistence orchestrate my business through notifications like this? Can we invalidate premature events?* 2




                        It seems to me that the raising an event here is essentially the same
                        thing as logging




                        Absolutely not. Logging is meant to have no side effects, however, I suspect your event UserCreated is likely to cause other business operations to happen. Like notifications. 3




                        it just says that such logic should be encapsulated in other classes,
                        and it is OK for a repository to call these other classes




                        Not necessarily true. SRP is not a class-specific concern only. It operates at different levels of abstractions, like layers, libraries and systems! It's about cohesion, about keeping together what changes for the same reasons. If the user creation (use case) changes it's likely the moment and the reasons for the event to happen also changes.





                        1: Naming things adequately also matters.



                        2: Say we sent UserCreated after _dataContext.SaveChanges();, but the whole database transaction failed later due to connection issues or constraints violations. Be careful with premature broadcasting of events, because its side effects can be hard to undo (if that is even possible).



                        3: Bear in mind that a sent email cannot be undon






                        share|improve this answer


























                        • +1 Very good point about the transaction span. It can be premature to assert the user has been created, because rollbacks can happen; and unlike with a log, it's likely some other part of the app does something with the event.

                          – Andres F.
                          13 hours ago






                        • 1





                          Exactly. Events denote certainity. Something happened but it's over.

                          – Laiv
                          13 hours ago













                        • @Laiv: Except when they don't. Microsoft has all sorts of events prefixed with Before or Preview that make no guarantees at all about certainty.

                          – Robert Harvey
                          12 hours ago











                        • Hmm. I have never seen this sort of events. Would you mind sharing some references?

                          – Laiv
                          12 hours ago
















                        1














                        While technically there's nothing wrong with repositories notifying events, I would suggest looking at it from a functional point of view where its convenience rises some concerns.




                        Creating users is one thing, its persistence a different one.




                        Premise of mine



                        Consider the previous premise before deciding whether the repository is the proper place to notify business events (regardless of the SRP). Note that I said business event because to me UserCreated has a different connotation than UserStored or UserAdded 1. I would also consider each of those events to be addressed to different audiences.



                        On one side, creating users is a business-specific rule that might or might not involve persistence. It might involve more business operations, involving more database/network operations. Operations the persistence layer is unaware of. The persistence layer doesn't have enough context to decide whether the use case ended successfully or not.



                        On the flip side, it's not necessarily true that _dataContext.SaveChanges(); has persisted the user successfully. It will depend on the database's transaction span. For instance, it could be true for databases like MongoDB, which transactions are atomic, but it could not, for traditional RDBMS implementing ACID transactions where there could be more transactions involved and yet to be committed.




                        Is this a valid point?




                        I would dare to say that, It's not only a matter of SRP (technically speaking), It's also a matter of convenience (functionally speaking). Is it convenient to cast business events from components that are not totally aware of the business operations in progress? Do they represent the right place as much as the right moment? Should the persistence orchestrate my business through notifications like this? Can we invalidate premature events?* 2




                        It seems to me that the raising an event here is essentially the same
                        thing as logging




                        Absolutely not. Logging is meant to have no side effects, however, I suspect your event UserCreated is likely to cause other business operations to happen. Like notifications. 3




                        it just says that such logic should be encapsulated in other classes,
                        and it is OK for a repository to call these other classes




                        Not necessarily true. SRP is not a class-specific concern only. It operates at different levels of abstractions, like layers, libraries and systems! It's about cohesion, about keeping together what changes for the same reasons. If the user creation (use case) changes it's likely the moment and the reasons for the event to happen also changes.





                        1: Naming things adequately also matters.



                        2: Say we sent UserCreated after _dataContext.SaveChanges();, but the whole database transaction failed later due to connection issues or constraints violations. Be careful with premature broadcasting of events, because its side effects can be hard to undo (if that is even possible).



                        3: Bear in mind that a sent email cannot be undon






                        share|improve this answer


























                        • +1 Very good point about the transaction span. It can be premature to assert the user has been created, because rollbacks can happen; and unlike with a log, it's likely some other part of the app does something with the event.

                          – Andres F.
                          13 hours ago






                        • 1





                          Exactly. Events denote certainity. Something happened but it's over.

                          – Laiv
                          13 hours ago













                        • @Laiv: Except when they don't. Microsoft has all sorts of events prefixed with Before or Preview that make no guarantees at all about certainty.

                          – Robert Harvey
                          12 hours ago











                        • Hmm. I have never seen this sort of events. Would you mind sharing some references?

                          – Laiv
                          12 hours ago














                        1












                        1








                        1







                        While technically there's nothing wrong with repositories notifying events, I would suggest looking at it from a functional point of view where its convenience rises some concerns.




                        Creating users is one thing, its persistence a different one.




                        Premise of mine



                        Consider the previous premise before deciding whether the repository is the proper place to notify business events (regardless of the SRP). Note that I said business event because to me UserCreated has a different connotation than UserStored or UserAdded 1. I would also consider each of those events to be addressed to different audiences.



                        On one side, creating users is a business-specific rule that might or might not involve persistence. It might involve more business operations, involving more database/network operations. Operations the persistence layer is unaware of. The persistence layer doesn't have enough context to decide whether the use case ended successfully or not.



                        On the flip side, it's not necessarily true that _dataContext.SaveChanges(); has persisted the user successfully. It will depend on the database's transaction span. For instance, it could be true for databases like MongoDB, which transactions are atomic, but it could not, for traditional RDBMS implementing ACID transactions where there could be more transactions involved and yet to be committed.




                        Is this a valid point?




                        I would dare to say that, It's not only a matter of SRP (technically speaking), It's also a matter of convenience (functionally speaking). Is it convenient to cast business events from components that are not totally aware of the business operations in progress? Do they represent the right place as much as the right moment? Should the persistence orchestrate my business through notifications like this? Can we invalidate premature events?* 2




                        It seems to me that the raising an event here is essentially the same
                        thing as logging




                        Absolutely not. Logging is meant to have no side effects, however, I suspect your event UserCreated is likely to cause other business operations to happen. Like notifications. 3




                        it just says that such logic should be encapsulated in other classes,
                        and it is OK for a repository to call these other classes




                        Not necessarily true. SRP is not a class-specific concern only. It operates at different levels of abstractions, like layers, libraries and systems! It's about cohesion, about keeping together what changes for the same reasons. If the user creation (use case) changes it's likely the moment and the reasons for the event to happen also changes.





                        1: Naming things adequately also matters.



                        2: Say we sent UserCreated after _dataContext.SaveChanges();, but the whole database transaction failed later due to connection issues or constraints violations. Be careful with premature broadcasting of events, because its side effects can be hard to undo (if that is even possible).



                        3: Bear in mind that a sent email cannot be undon






                        share|improve this answer















                        While technically there's nothing wrong with repositories notifying events, I would suggest looking at it from a functional point of view where its convenience rises some concerns.




                        Creating users is one thing, its persistence a different one.




                        Premise of mine



                        Consider the previous premise before deciding whether the repository is the proper place to notify business events (regardless of the SRP). Note that I said business event because to me UserCreated has a different connotation than UserStored or UserAdded 1. I would also consider each of those events to be addressed to different audiences.



                        On one side, creating users is a business-specific rule that might or might not involve persistence. It might involve more business operations, involving more database/network operations. Operations the persistence layer is unaware of. The persistence layer doesn't have enough context to decide whether the use case ended successfully or not.



                        On the flip side, it's not necessarily true that _dataContext.SaveChanges(); has persisted the user successfully. It will depend on the database's transaction span. For instance, it could be true for databases like MongoDB, which transactions are atomic, but it could not, for traditional RDBMS implementing ACID transactions where there could be more transactions involved and yet to be committed.




                        Is this a valid point?




                        I would dare to say that, It's not only a matter of SRP (technically speaking), It's also a matter of convenience (functionally speaking). Is it convenient to cast business events from components that are not totally aware of the business operations in progress? Do they represent the right place as much as the right moment? Should the persistence orchestrate my business through notifications like this? Can we invalidate premature events?* 2




                        It seems to me that the raising an event here is essentially the same
                        thing as logging




                        Absolutely not. Logging is meant to have no side effects, however, I suspect your event UserCreated is likely to cause other business operations to happen. Like notifications. 3




                        it just says that such logic should be encapsulated in other classes,
                        and it is OK for a repository to call these other classes




                        Not necessarily true. SRP is not a class-specific concern only. It operates at different levels of abstractions, like layers, libraries and systems! It's about cohesion, about keeping together what changes for the same reasons. If the user creation (use case) changes it's likely the moment and the reasons for the event to happen also changes.





                        1: Naming things adequately also matters.



                        2: Say we sent UserCreated after _dataContext.SaveChanges();, but the whole database transaction failed later due to connection issues or constraints violations. Be careful with premature broadcasting of events, because its side effects can be hard to undo (if that is even possible).



                        3: Bear in mind that a sent email cannot be undon







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited 1 hour ago

























                        answered 14 hours ago









                        LaivLaiv

                        6,89311241




                        6,89311241













                        • +1 Very good point about the transaction span. It can be premature to assert the user has been created, because rollbacks can happen; and unlike with a log, it's likely some other part of the app does something with the event.

                          – Andres F.
                          13 hours ago






                        • 1





                          Exactly. Events denote certainity. Something happened but it's over.

                          – Laiv
                          13 hours ago













                        • @Laiv: Except when they don't. Microsoft has all sorts of events prefixed with Before or Preview that make no guarantees at all about certainty.

                          – Robert Harvey
                          12 hours ago











                        • Hmm. I have never seen this sort of events. Would you mind sharing some references?

                          – Laiv
                          12 hours ago



















                        • +1 Very good point about the transaction span. It can be premature to assert the user has been created, because rollbacks can happen; and unlike with a log, it's likely some other part of the app does something with the event.

                          – Andres F.
                          13 hours ago






                        • 1





                          Exactly. Events denote certainity. Something happened but it's over.

                          – Laiv
                          13 hours ago













                        • @Laiv: Except when they don't. Microsoft has all sorts of events prefixed with Before or Preview that make no guarantees at all about certainty.

                          – Robert Harvey
                          12 hours ago











                        • Hmm. I have never seen this sort of events. Would you mind sharing some references?

                          – Laiv
                          12 hours ago

















                        +1 Very good point about the transaction span. It can be premature to assert the user has been created, because rollbacks can happen; and unlike with a log, it's likely some other part of the app does something with the event.

                        – Andres F.
                        13 hours ago





                        +1 Very good point about the transaction span. It can be premature to assert the user has been created, because rollbacks can happen; and unlike with a log, it's likely some other part of the app does something with the event.

                        – Andres F.
                        13 hours ago




                        1




                        1





                        Exactly. Events denote certainity. Something happened but it's over.

                        – Laiv
                        13 hours ago







                        Exactly. Events denote certainity. Something happened but it's over.

                        – Laiv
                        13 hours ago















                        @Laiv: Except when they don't. Microsoft has all sorts of events prefixed with Before or Preview that make no guarantees at all about certainty.

                        – Robert Harvey
                        12 hours ago





                        @Laiv: Except when they don't. Microsoft has all sorts of events prefixed with Before or Preview that make no guarantees at all about certainty.

                        – Robert Harvey
                        12 hours ago













                        Hmm. I have never seen this sort of events. Would you mind sharing some references?

                        – Laiv
                        12 hours ago





                        Hmm. I have never seen this sort of events. Would you mind sharing some references?

                        – Laiv
                        12 hours ago











                        0














                        Currently SaveChanges does two things: it saves the changes and logs that it does so. Now you want to add another thing to it: send email notifications.



                        You had the clever idea to add an event to it, but this was criticised for violating the Single Responsibility Principle (SRP), without noticing that it had already been violated.



                        To get a pure SRP solution, first trigger the event, then call all the hooks for that event, of which there are now three: saving, logging, and finally sending emails.



                        Either you trigger the event first, or you have to add to SaveChanges. Your solution is a hybrid between the two. It doesn't address the existing violation while it does encourage preventing it from increasing beyond three things. Refactoring the existing code to comply with SRP might require more work than is strictly necessary. It's up to your project how far they want to take SRP.






                        share|improve this answer




























                          0














                          Currently SaveChanges does two things: it saves the changes and logs that it does so. Now you want to add another thing to it: send email notifications.



                          You had the clever idea to add an event to it, but this was criticised for violating the Single Responsibility Principle (SRP), without noticing that it had already been violated.



                          To get a pure SRP solution, first trigger the event, then call all the hooks for that event, of which there are now three: saving, logging, and finally sending emails.



                          Either you trigger the event first, or you have to add to SaveChanges. Your solution is a hybrid between the two. It doesn't address the existing violation while it does encourage preventing it from increasing beyond three things. Refactoring the existing code to comply with SRP might require more work than is strictly necessary. It's up to your project how far they want to take SRP.






                          share|improve this answer


























                            0












                            0








                            0







                            Currently SaveChanges does two things: it saves the changes and logs that it does so. Now you want to add another thing to it: send email notifications.



                            You had the clever idea to add an event to it, but this was criticised for violating the Single Responsibility Principle (SRP), without noticing that it had already been violated.



                            To get a pure SRP solution, first trigger the event, then call all the hooks for that event, of which there are now three: saving, logging, and finally sending emails.



                            Either you trigger the event first, or you have to add to SaveChanges. Your solution is a hybrid between the two. It doesn't address the existing violation while it does encourage preventing it from increasing beyond three things. Refactoring the existing code to comply with SRP might require more work than is strictly necessary. It's up to your project how far they want to take SRP.






                            share|improve this answer













                            Currently SaveChanges does two things: it saves the changes and logs that it does so. Now you want to add another thing to it: send email notifications.



                            You had the clever idea to add an event to it, but this was criticised for violating the Single Responsibility Principle (SRP), without noticing that it had already been violated.



                            To get a pure SRP solution, first trigger the event, then call all the hooks for that event, of which there are now three: saving, logging, and finally sending emails.



                            Either you trigger the event first, or you have to add to SaveChanges. Your solution is a hybrid between the two. It doesn't address the existing violation while it does encourage preventing it from increasing beyond three things. Refactoring the existing code to comply with SRP might require more work than is strictly necessary. It's up to your project how far they want to take SRP.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered 6 hours ago









                            CJ DennisCJ Dennis

                            38717




                            38717






























                                draft saved

                                draft discarded




















































                                Thanks for contributing an answer to Software Engineering Stack Exchange!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid



                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.


                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function () {
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f389237%2fapplicability-of-single-responsibility-principle%23new-answer', 'question_page');
                                }
                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown











                                Popular posts from this blog

                                Færeyskur hestur Heimild | Tengill | Tilvísanir | LeiðsagnarvalRossið - síða um færeyska hrossið á færeyskuGott ár hjá færeyska hestinum

                                He _____ here since 1970 . Answer needed [closed]What does “since he was so high” mean?Meaning of “catch birds for”?How do I ensure “since” takes the meaning I want?“Who cares here” meaningWhat does “right round toward” mean?the time tense (had now been detected)What does the phrase “ring around the roses” mean here?Correct usage of “visited upon”Meaning of “foiled rail sabotage bid”It was the third time I had gone to Rome or It is the third time I had been to Rome

                                Slayer Innehåll Historia | Stil, komposition och lyrik | Bandets betydelse och framgångar | Sidoprojekt och samarbeten | Kontroverser | Medlemmar | Utmärkelser och nomineringar | Turnéer och festivaler | Diskografi | Referenser | Externa länkar | Navigeringsmenywww.slayer.net”Metal Massacre vol. 1””Metal Massacre vol. 3””Metal Massacre Volume III””Show No Mercy””Haunting the Chapel””Live Undead””Hell Awaits””Reign in Blood””Reign in Blood””Gold & Platinum – Reign in Blood””Golden Gods Awards Winners”originalet”Kerrang! Hall Of Fame””Slayer Looks Back On 37-Year Career In New Video Series: Part Two””South of Heaven””Gold & Platinum – South of Heaven””Seasons in the Abyss””Gold & Platinum - Seasons in the Abyss””Divine Intervention””Divine Intervention - Release group by Slayer””Gold & Platinum - Divine Intervention””Live Intrusion””Undisputed Attitude””Abolish Government/Superficial Love””Release “Slatanic Slaughter: A Tribute to Slayer” by Various Artists””Diabolus in Musica””Soundtrack to the Apocalypse””God Hates Us All””Systematic - Relationships””War at the Warfield””Gold & Platinum - War at the Warfield””Soundtrack to the Apocalypse””Gold & Platinum - Still Reigning””Metallica, Slayer, Iron Mauden Among Winners At Metal Hammer Awards””Eternal Pyre””Eternal Pyre - Slayer release group””Eternal Pyre””Metal Storm Awards 2006””Kerrang! Hall Of Fame””Slayer Wins 'Best Metal' Grammy Award””Slayer Guitarist Jeff Hanneman Dies””Bullet-For My Valentine booed at Metal Hammer Golden Gods Awards””Unholy Aliance””The End Of Slayer?””Slayer: We Could Thrash Out Two More Albums If We're Fast Enough...””'The Unholy Alliance: Chapter III' UK Dates Added”originalet”Megadeth And Slayer To Co-Headline 'Canadian Carnage' Trek”originalet”World Painted Blood””Release “World Painted Blood” by Slayer””Metallica Heading To Cinemas””Slayer, Megadeth To Join Forces For 'European Carnage' Tour - Dec. 18, 2010”originalet”Slayer's Hanneman Contracts Acute Infection; Band To Bring In Guest Guitarist””Cannibal Corpse's Pat O'Brien Will Step In As Slayer's Guest Guitarist”originalet”Slayer’s Jeff Hanneman Dead at 49””Dave Lombardo Says He Made Only $67,000 In 2011 While Touring With Slayer””Slayer: We Do Not Agree With Dave Lombardo's Substance Or Timeline Of Events””Slayer Welcomes Drummer Paul Bostaph Back To The Fold””Slayer Hope to Unveil Never-Before-Heard Jeff Hanneman Material on Next Album””Slayer Debut New Song 'Implode' During Surprise Golden Gods Appearance””Release group Repentless by Slayer””Repentless - Slayer - Credits””Slayer””Metal Storm Awards 2015””Slayer - to release comic book "Repentless #1"””Slayer To Release 'Repentless' 6.66" Vinyl Box Set””BREAKING NEWS: Slayer Announce Farewell Tour””Slayer Recruit Lamb of God, Anthrax, Behemoth + Testament for Final Tour””Slayer lägger ner efter 37 år””Slayer Announces Second North American Leg Of 'Final' Tour””Final World Tour””Slayer Announces Final European Tour With Lamb of God, Anthrax And Obituary””Slayer To Tour Europe With Lamb of God, Anthrax And Obituary””Slayer To Play 'Last French Show Ever' At Next Year's Hellfst””Slayer's Final World Tour Will Extend Into 2019””Death Angel's Rob Cavestany On Slayer's 'Farewell' Tour: 'Some Of Us Could See This Coming'””Testament Has No Plans To Retire Anytime Soon, Says Chuck Billy””Anthrax's Scott Ian On Slayer's 'Farewell' Tour Plans: 'I Was Surprised And I Wasn't Surprised'””Slayer””Slayer's Morbid Schlock””Review/Rock; For Slayer, the Mania Is the Message””Slayer - Biography””Slayer - Reign In Blood”originalet”Dave Lombardo””An exclusive oral history of Slayer”originalet”Exclusive! Interview With Slayer Guitarist Jeff Hanneman”originalet”Thinking Out Loud: Slayer's Kerry King on hair metal, Satan and being polite””Slayer Lyrics””Slayer - Biography””Most influential artists for extreme metal music””Slayer - Reign in Blood””Slayer guitarist Jeff Hanneman dies aged 49””Slatanic Slaughter: A Tribute to Slayer””Gateway to Hell: A Tribute to Slayer””Covered In Blood””Slayer: The Origins of Thrash in San Francisco, CA.””Why They Rule - #6 Slayer”originalet”Guitar World's 100 Greatest Heavy Metal Guitarists Of All Time”originalet”The fans have spoken: Slayer comes out on top in readers' polls”originalet”Tribute to Jeff Hanneman (1964-2013)””Lamb Of God Frontman: We Sound Like A Slayer Rip-Off””BEHEMOTH Frontman Pays Tribute To SLAYER's JEFF HANNEMAN””Slayer, Hatebreed Doing Double Duty On This Year's Ozzfest””System of a Down””Lacuna Coil’s Andrea Ferro Talks Influences, Skateboarding, Band Origins + More””Slayer - Reign in Blood””Into The Lungs of Hell””Slayer rules - en utställning om fans””Slayer and Their Fans Slashed Through a No-Holds-Barred Night at Gas Monkey””Home””Slayer””Gold & Platinum - The Big 4 Live from Sofia, Bulgaria””Exclusive! Interview With Slayer Guitarist Kerry King””2008-02-23: Wiltern, Los Angeles, CA, USA””Slayer's Kerry King To Perform With Megadeth Tonight! - Oct. 21, 2010”originalet”Dave Lombardo - Biography”Slayer Case DismissedArkiveradUltimate Classic Rock: Slayer guitarist Jeff Hanneman dead at 49.”Slayer: "We could never do any thing like Some Kind Of Monster..."””Cannibal Corpse'S Pat O'Brien Will Step In As Slayer'S Guest Guitarist | The Official Slayer Site”originalet”Slayer Wins 'Best Metal' Grammy Award””Slayer Guitarist Jeff Hanneman Dies””Kerrang! Awards 2006 Blog: Kerrang! Hall Of Fame””Kerrang! Awards 2013: Kerrang! Legend”originalet”Metallica, Slayer, Iron Maien Among Winners At Metal Hammer Awards””Metal Hammer Golden Gods Awards””Bullet For My Valentine Booed At Metal Hammer Golden Gods Awards””Metal Storm Awards 2006””Metal Storm Awards 2015””Slayer's Concert History””Slayer - Relationships””Slayer - Releases”Slayers officiella webbplatsSlayer på MusicBrainzOfficiell webbplatsSlayerSlayerr1373445760000 0001 1540 47353068615-5086262726cb13906545x(data)6033143kn20030215029