5

I am currently starting a new project with a 4-tier architecture design. The layers is set as follow.

                                          +------------------+
                         +----------------+   Presentation   |
                         |                +------------------+
                         |                         |
                         |                         |
                         |         +---------------v----------------+
                         |         |                                |
                         |         |         Services (dll)         |
                         |         |                                |
                         |   +-----+  +------+  +------+  +------+  |
                         |   |     |  | Log  <-->Print <--+Other |  |
                         |   |     |  +------+  +------+  +------+  |
                         |   |     |                                |
                         |   |     +--------------------------------+
                         |   |                     |
                         |   |                     |
                         |   |            +--------v---------+
                         |   |   +--------+  Business Logic  |
                         |   |   |        +------------------+
                         |   |   |                 |
                        +v---v---v+                |
                        |         |       +--------v---------+
                        |   DTO   <-------+   Data Access    |
                        |         |       +------------------+
                        +---------+

In service Layer, I encountered a circular referencing problem as

  1. Print Service references Log service when it need to log the print task down.
  2. Log Service references Print Servicewhen it need to get some printing detail via some method in Print Service

All of the services and other tiers/layers is existed as a csproj, so it will be compiled as a dll.

I would like to know how can I get rid of such scenario? After doing some research, the most possible solution is to create an interface project for each service. Is there any other better solution?

If I really adopt interface projects approach as a solution, Does it mean that I must go along with Dependency Injection? Dependency Injection is powerful, but it makes codes to be more difficult to trace too!

Lastly, as Presentation Layer cannot reference BLL directly. Therefore, some classes and models defined in BLL cannot be referenced. Is it a good idea to store those classes and models in DTO project so that it can be used by upper-indirectly-referenced layers? (Of course I will rename the DTO project to some more meaningful name e.g. BusinessObjects)

UPDATE 1

I know my example may not be good enough. Let me change the requirement.

CourseService and StudentService this time. The dependency is visualized as follow

                            +--------------------------------+
                            |                                |
                            |         Services (DLL)         |
                            |                                |
                            |   +-------+        +-------+   |
                            |   |Course <-------->Student|   |
                            |   +-------+        +-------+   |
                            |                                |
                            +--------------------------------+
  1. CourseService has a method GetEligibleStudent(Course crs) to list Student[] those are qualified to enroll certain Course base on some course-specific information and complex logic in StudentService.
  2. StudentService need to call PrintGrade(Course crs, Student stu) to retrieve certain course grade of the a student.

Seems this example is better. I know someone may challenge that GetEligibleStudent(Course crs) should be placed in StudentService or PrintGrade(Course crs, Student stu) should be placed in CourseService. However, circular dependency may exist anytime in development. An appropriate solution should be took, not just in my example.

12
  • why would log service need printing details? to do formatting? if yes, it shouldn't, that's the print service's job. if for the call in 1 - pass all necessary data, or pass a reference to the print service object, so the log service can question it. but I don't see for what it would need that.
    – Aganju
    Commented Nov 17, 2018 at 21:58
  • Thanks for your attention. I get your point. Maybe this example is not good enough. I just want to bring out how do I handle such circular dependency case. I try to use a better example instead.
    – mannok
    Commented Nov 17, 2018 at 22:02
  • 2
    The most simple solution seems to have an interface.
    – Konrads
    Commented Nov 17, 2018 at 23:48
  • 1
    I think solution would be remove Print dependency from the Log service. Print service required Log and will provide all details to the Log, without Log service knowing about Print
    – Fabio
    Commented Nov 18, 2018 at 0:37
  • 2
    Another approach is to follow "rule" that when you have circular dependency you will introduce third separated library which will combine two others.
    – Fabio
    Commented Nov 18, 2018 at 1:01

1 Answer 1

2

There is a clear data relationship of Course with Student. That's where primary domain modelling should terminate. If not, it infects design decision making for the worse.

Orchestration instead

Generally people forget about "orchestration" (or they were not taught it). If you stick with entity thinking, all you do is model entities everywhere. Entities are data, but you need to deal with "information".

So in this case:

  • Course is an entity
  • Student is an entity
  • GetEligibleStudents becomes - GetEligibleStudents(int CourseId) in an Orchestration service called something like CoursePolicy
  • PrintGrade become - PrintToPDF(int CourseId, int StudentId) in an Orchestration service called something like GradeResult

There are also potential benefits in modelling: CoursePolicy as an Entity - where the policy configuration can also be stored; and, GradeResult - where the Grade stores the grade result itself and can be retrieved in different forms. But we only consider this now, because we first considered "orchestration" and broke away from the original entities.

This applies in many other cases too of course.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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