Help creating a flexible base 'find' method in a service class using the DRY principle

后端 未结 1 751
[愿得一人]
[愿得一人] 2021-02-10 02:14

For years now I\'ve been reimplementing the same code over and over (with evolution) without finding some method of cleanly, and efficiently, abstracting it out.

The pat

相关标签:
1条回答
  • 2021-02-10 03:15

    I think you're overcomplicating things.

    I've worked on a project using Doctrine 2 which has quite a lot of entities, different uses for them, various services, custom repositories etc. and I've found something like this works rather well (for me anyway)..

    1. Repositories for queries

    Firstly, I don't generally do queries in services. Doctrine 2 provides the EntityRepository and the option of subclassing it for each entity for this exact purpose.

    • Whenever possible, I use the standard findOneBy... and findBy... style magic methods. This saves me from having to write DQL myself and works rather nicely out of the box.
    • If I need more complicated querying logic, I usually create use-case specific finders in the repositories. These are things like UserRepository.findByNameStartsWith and things like that.
    • I generally don't create a super fancy "I can take any args you give me!" type of magic finders. If I need a specific query, I add a specific method. While this may seem like it requires you to write more code, I think it's a much simpler and easier to understand way of doing things. (I tried going through your finder code and it was rather complicated looking in places)

    So in other words...

    • Try to use what doctrine already gives you (magic finder methods)
    • Use custom repository classes if you need custom querying logic
    • Create a method per query type

    2. Services for combining non-entity logic

    Use services to combine "transactions" behind a simple interface you can use from your controllers or test easily with unit tests.

    For example, let's say your users can add friends. Whenever a user friends someone else, an email is dispatched to the other person to notify. This is something you would have in your service.

    Your service would (for example) include a method addNewFriend which takes two users. Then, it could use a repository to query for some data, update the users' friend arrays, and call some other class which then sends the email.

    You can use the entitymanager in your services for getting repository classes or persisting entities.

    3. Entities for entity-specific logic

    Lastly you should try to put your business logic that is specific to an entity directly into the entity class.

    A simple example for this case could be that maybe the email sending in the above scenario uses some sort of a greeting.. "Hello Mr. Anderson", or "Hello Ms. Anderson".

    So for example you would need some logic to determine the appropriate greeting. This is something you could have in the entity class - For example, getGreeting or something, which could then take into account the user's gender and nationality and return something based on that. (assuming gender and nationality would be stored in the database, but not the greeting itself - the greeting would be calculated by the function's logic)

    I should probably also point out that the entities should generally not know of either the entitymanager or the repositories. If the logic requires either of these, it probably doesn't belong into the entity class itself.

    Benefits of this approach

    I have found the approach I've detailed here works rather well. It's maintainable because it generally is quite "obvious" to what things do, it doesn't depend on complicated querying behavior, and because things are split clearly into different "areas" (repos, services, entities) it's quite straightforward to unit test as well.

    0 讨论(0)
提交回复
热议问题