Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. Design and Architecture
  4. Avoiding Circular References with Application Architecture

Avoiding Circular References with Application Architecture

Scheduled Pinned Locked Moved Design and Architecture
questiondatabasedesignarchitecturehelp
20 Posts 4 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Leftyfarrell

    Thanks Jon, was hoping to get your opinion. I considered that, but worried that it wasn't the "best" or "proper" solution, since the circular reference would technically still be there right? It would just be hidden on one side by the interface. So the Common API would still reference and call the Business Layer for data. All other assemblies would reference an Common Interfaces project and their Common instance would be provided by Unity. Something like that? Again, each one still references the other directly or indirectly and that is what I wasn't sure about.

    J Offline
    J Offline
    Jon Rista
    wrote on last edited by
    #7

    The key thing is removing the direct dependency cycle. Those are the things that can really kill you during maintenance or extension. Indirect dependencies are not nearly as worriesom, however there are times that you might want to keep things completely independant. In the case of cross-cutting concerns, as long as your utilizing your common API, and injecting the dependencies via configuration (this is called Inversion of Control and Dependency Injection), then the implementation can still vary independently from all the code that is actually using your cross-cutting API. Logging, validation, instrumentation, security, etc. are all generally cross-cutting, and making changes to how they are used has the broadest reach of any change in most systems. If you change how your DAL works...you only really need to modify the next layer up...and possibly only part of it, to compensate. However, if you change how your logging works, you will likely have to touch just about every method you have written. When you sit down to write your API's for your cross-cutting frameworks, think about them for a while and make sure you cover as many scenarios, even if they don't quite exist yet, to prevent yourself from having to modify your CCAPI's in the future. Once you have the API...you are free to implement it however you want. You can utilize your standard DAL for logging, validation, and localization...or you can utilize a custom DAL specific to them. Whichever one meets your needs and minimizes impact to long term maintenance.

    1 Reply Last reply
    0
    • L led mike

      I see John has already replied. If you look at the patterns you are using you will notice they all use interfaces to avoid the coupling issue you are dealing with, just as John points out. Interfaces eliminate coupling, take a look at the mediator pattern. Organizing libraries / assemblies requires similar structure to avoid dependencies between the modules.

      led mike

      J Offline
      J Offline
      Jon Rista
      wrote on last edited by
      #8

      led mike wrote:

      Interfaces eliminate coupling

      I just thought I would point out something important. Contrary to common opinions, interfaces do not really "eliminate" coupling. Coupling exists, and its such a thing that can't really be eliminated. It can be mitigated, and interfaces can help mitigate coupling. When we directly create and use a concrete implementation class, we are "tightly coupled" to an implementation (the consumers tightly couple with their provider), because we are bound not only to an API but a specific implementation. Interfaces allow us to reduce the degree of coupling, since when we create an interface, we are "loosely coupled" through the interface to an implementation. So long as the interface doesn't change, we can modify the concrete class, replace it with another, etc. without requiring modification of the code that uses the interface. We can gain the same thing with abstract classes (technically speaking, an abstract class with zero implementation is an interface). But its important to note...you ARE "tightly coupled" to the interface. Coupling also comes in a variety of forms, and can be encountered at different levels of a project as well. One class can be coupled to another (tightly via direct instantiation, or loosely via interface/abstract). One assembly can be coupled to another assembly. A layer in an architecure is coupled to another layer, as well as to cross-cutting concerns. Services can be coupled to other services. Code can be coupled in time to the running length of other code. Coupling is a very broad issue for software architecture, and new aspects of coupling are always being discovered as we discover new ways to write software and solve other problems. Key thing though...coupling can never really be eliminated. Your always coupled to something, but what that something is determines whether the coupling is manageable or not. Thats why most (if not all) problems in software architecure can generally be solved by abstracting another degree...separating one thing from another via an API of some kind increases independant variability and reduces coupling.

      L 1 Reply Last reply
      0
      • J Jon Rista

        led mike wrote:

        Interfaces eliminate coupling

        I just thought I would point out something important. Contrary to common opinions, interfaces do not really "eliminate" coupling. Coupling exists, and its such a thing that can't really be eliminated. It can be mitigated, and interfaces can help mitigate coupling. When we directly create and use a concrete implementation class, we are "tightly coupled" to an implementation (the consumers tightly couple with their provider), because we are bound not only to an API but a specific implementation. Interfaces allow us to reduce the degree of coupling, since when we create an interface, we are "loosely coupled" through the interface to an implementation. So long as the interface doesn't change, we can modify the concrete class, replace it with another, etc. without requiring modification of the code that uses the interface. We can gain the same thing with abstract classes (technically speaking, an abstract class with zero implementation is an interface). But its important to note...you ARE "tightly coupled" to the interface. Coupling also comes in a variety of forms, and can be encountered at different levels of a project as well. One class can be coupled to another (tightly via direct instantiation, or loosely via interface/abstract). One assembly can be coupled to another assembly. A layer in an architecure is coupled to another layer, as well as to cross-cutting concerns. Services can be coupled to other services. Code can be coupled in time to the running length of other code. Coupling is a very broad issue for software architecture, and new aspects of coupling are always being discovered as we discover new ways to write software and solve other problems. Key thing though...coupling can never really be eliminated. Your always coupled to something, but what that something is determines whether the coupling is manageable or not. Thats why most (if not all) problems in software architecure can generally be solved by abstracting another degree...separating one thing from another via an API of some kind increases independant variability and reduces coupling.

        L Offline
        L Offline
        led mike
        wrote on last edited by
        #9

        Jon Rista wrote:

        Key thing though...coupling can never really be eliminated.

        Yes, bad choice of words "eliminate", loose coupling is the proper description. On this site I have not run into a lot of people that understand terms like that. I guess I have started dumbing down my replies, which is really dangerous since I'm already dumb enough to be dangerous. ;P

        led mike

        1 Reply Last reply
        0
        • L Leftyfarrell

          I would like to hear comments and suggestions on the proper way to avoid circular references between projects. Consider an app with the following assemblies: - UI - BusinessLogic - DAL - Common So typically, the dependencies would look like this: UI >> BusinessLogic >> DAL and all 3 above depend on Common So far, so good, but... lets say that I have a common collection of data that is required to be used in all 3 of the main assemblies, such as a list of supported cultures. So I create a static helper class in the Common assembly and this gives me access to the shared singleton collection from all 3 main assemblies. However, to populate that singleton collection when the app starts, I need to hit the database once. To do this right, I would load the collection from the database using my BusinessLogic layer. But I cannot do this because doing so would cause a circular reference between Common and BusinessLogic. What is the best architecture to avoid/work around this problem? Thanks.

          M Offline
          M Offline
          Moim Hossain
          wrote on last edited by
          #10

          Leftyfarrell wrote:

          What is the best architecture to avoid/work around this problem?

          Don't know how you will take this, but my opinion will be as follows, The common will contain only the DTO structure of the data you are concerned about. for example you common can have a class roughly something like

          namespace Common {
          public class SupportedCultures : Collection {}
          }

          // now you DAL should have an interface like

          ICultureDal { Common.SupportedCultures GetSupportedCultures();}

          //Now, in the buiness layer,

          IBusinessServices
          {
          Common.SupportedCultures GetSupportedCultures();// it will use the ICultureDal to get the data
          }

          now you client application will use the IBusinessServices to get the list of culture and may be cache it as needed to avoid further business calls. (you can also cache it at your service layer)

          Moim Hossain R&D Project Manager BlueCielo ECM Solutions BV

          J 1 Reply Last reply
          0
          • M Moim Hossain

            Leftyfarrell wrote:

            What is the best architecture to avoid/work around this problem?

            Don't know how you will take this, but my opinion will be as follows, The common will contain only the DTO structure of the data you are concerned about. for example you common can have a class roughly something like

            namespace Common {
            public class SupportedCultures : Collection {}
            }

            // now you DAL should have an interface like

            ICultureDal { Common.SupportedCultures GetSupportedCultures();}

            //Now, in the buiness layer,

            IBusinessServices
            {
            Common.SupportedCultures GetSupportedCultures();// it will use the ICultureDal to get the data
            }

            now you client application will use the IBusinessServices to get the list of culture and may be cache it as needed to avoid further business calls. (you can also cache it at your service layer)

            Moim Hossain R&D Project Manager BlueCielo ECM Solutions BV

            J Offline
            J Offline
            Jon Rista
            wrote on last edited by
            #11

            That works for core concerns...business concerns. But the original question was in regards to cross-cutting concerns. Cross-cutting concerns are things like logging, validation, instrumentation, security, context, etc. These don't really fall into the categories of business services or DTOs. Cross-cutting concerns are one of the major pain points in software development today, and the driving force behing things like AOP, Aspect-Oriented Programming. AOP aims to privide a mechanism to separate cross-cutting concerns from your core concerns, and apply them in an alternative, configurational manner (possibly adjustable at runtime).

            L 1 Reply Last reply
            0
            • J Jon Rista

              That works for core concerns...business concerns. But the original question was in regards to cross-cutting concerns. Cross-cutting concerns are things like logging, validation, instrumentation, security, context, etc. These don't really fall into the categories of business services or DTOs. Cross-cutting concerns are one of the major pain points in software development today, and the driving force behing things like AOP, Aspect-Oriented Programming. AOP aims to privide a mechanism to separate cross-cutting concerns from your core concerns, and apply them in an alternative, configurational manner (possibly adjustable at runtime).

              L Offline
              L Offline
              led mike
              wrote on last edited by
              #12

              Are you certain the OP's topic of loading a collection of supported cultures fits that category? Doesn't seem like it should. I am always suspicious that a static helper class is a indication that some refactoring might be in order.

              Leftyfarrell wrote:

              So I create a static helper class

              led mike

              J 1 Reply Last reply
              0
              • L led mike

                Are you certain the OP's topic of loading a collection of supported cultures fits that category? Doesn't seem like it should. I am always suspicious that a static helper class is a indication that some refactoring might be in order.

                Leftyfarrell wrote:

                So I create a static helper class

                led mike

                J Offline
                J Offline
                Jon Rista
                wrote on last edited by
                #13

                Well, my other response described my recommended solution. I agree, statics are something that should generally be avoided. An area where statics can be helpful is in creating simple facades that wrap up complex frameworks into simpler ones. There is always a cost involved in every decision like that though.

                Leftyfarrell wrote:

                The heart of my question really has to do with cross cutting concerns like Logging, Exception Handling, Validation, Instrumentation etc.

                The above statement is what I am basing all of this on. Cross-cutting concerns are those that permate every aspect of your application, all layers, all levels. They are difficult to neatly package up into well-isolated "areas" like a presentation or business layer, a service, whatever. Lefty's validation loads localized messages from a database, caches them in a static class, and spits them out whenever a validation error occurrs. Even though localized text is involved, it doesn't change the fact that were talking about a cross-cutting concern. Its just a cross-cutting concern that happens to access a database. The choice to use a static class to hold the cached localization data is probably fine...perhapse even ideal, if the messages rarely or never change. Data that seldom changes is technically static anyway.

                L 1 Reply Last reply
                0
                • J Jon Rista

                  Well, my other response described my recommended solution. I agree, statics are something that should generally be avoided. An area where statics can be helpful is in creating simple facades that wrap up complex frameworks into simpler ones. There is always a cost involved in every decision like that though.

                  Leftyfarrell wrote:

                  The heart of my question really has to do with cross cutting concerns like Logging, Exception Handling, Validation, Instrumentation etc.

                  The above statement is what I am basing all of this on. Cross-cutting concerns are those that permate every aspect of your application, all layers, all levels. They are difficult to neatly package up into well-isolated "areas" like a presentation or business layer, a service, whatever. Lefty's validation loads localized messages from a database, caches them in a static class, and spits them out whenever a validation error occurrs. Even though localized text is involved, it doesn't change the fact that were talking about a cross-cutting concern. Its just a cross-cutting concern that happens to access a database. The choice to use a static class to hold the cached localization data is probably fine...perhapse even ideal, if the messages rarely or never change. Data that seldom changes is technically static anyway.

                  L Offline
                  L Offline
                  led mike
                  wrote on last edited by
                  #14

                  Jon Rista wrote:

                  They are difficult to neatly package up into well-isolated "areas"

                  I guess I don't understand, I have never run into any unusual problems isolating the concerns you specified. Here I use the word isolate regarding both a design and packaging perspective, which avoids any circular issues.

                  led mike

                  J 1 Reply Last reply
                  0
                  • L led mike

                    Jon Rista wrote:

                    They are difficult to neatly package up into well-isolated "areas"

                    I guess I don't understand, I have never run into any unusual problems isolating the concerns you specified. Here I use the word isolate regarding both a design and packaging perspective, which avoids any circular issues.

                    led mike

                    J Offline
                    J Offline
                    Jon Rista
                    wrote on last edited by
                    #15

                    I mean it from the perspective of use I guess: When you write a Controller, you put that controller in a project that is part of the "presentation layer" of your application. All controllers will be in the presentation layer...you won't have any in the business or data access layers. You have neatly grouped all your controllers into a single area, and they will only be USED by other things in your presentation layer. When you write a Business object, you put that service in a project that is part of the "business layer" of your application. All services will be in your business layer...you won't have any in the data access layer or in the presentation layer. You will transform business objects or graphs into DTO's for transfer accross the wire to your presentation layer and back. You have neatly grouped all your business objects into a single area, and they will only be USED by other things in your business layer. Cross-cutting concerns can not be isolated like that: When you write a logging framework, you put the logger class and its support types in a "shared" area. Your logging functionality is neatly encapsulated in the shared area of your application. Your logger will be used in MANY places, accross your layers, including presentation, business, and data access layers. Your logger's use is not neatly grouped, it is scattered throughout your application, and there is no way to keep usage of logging, a cross-cutting concern, isolated to a single area of your application.

                    L 1 Reply Last reply
                    0
                    • J Jon Rista

                      I mean it from the perspective of use I guess: When you write a Controller, you put that controller in a project that is part of the "presentation layer" of your application. All controllers will be in the presentation layer...you won't have any in the business or data access layers. You have neatly grouped all your controllers into a single area, and they will only be USED by other things in your presentation layer. When you write a Business object, you put that service in a project that is part of the "business layer" of your application. All services will be in your business layer...you won't have any in the data access layer or in the presentation layer. You will transform business objects or graphs into DTO's for transfer accross the wire to your presentation layer and back. You have neatly grouped all your business objects into a single area, and they will only be USED by other things in your business layer. Cross-cutting concerns can not be isolated like that: When you write a logging framework, you put the logger class and its support types in a "shared" area. Your logging functionality is neatly encapsulated in the shared area of your application. Your logger will be used in MANY places, accross your layers, including presentation, business, and data access layers. Your logger's use is not neatly grouped, it is scattered throughout your application, and there is no way to keep usage of logging, a cross-cutting concern, isolated to a single area of your application.

                      L Offline
                      L Offline
                      led mike
                      wrote on last edited by
                      #16

                      Jon Rista wrote:

                      Cross-cutting concerns can not be isolated like that:

                      Ah, yes because then it would not be cross-cutting. I thought we were talking about the library design creating the circular problem not the user code creating the circular problem. My bad.

                      led mike

                      J 1 Reply Last reply
                      0
                      • L led mike

                        Jon Rista wrote:

                        Cross-cutting concerns can not be isolated like that:

                        Ah, yes because then it would not be cross-cutting. I thought we were talking about the library design creating the circular problem not the user code creating the circular problem. My bad.

                        led mike

                        J Offline
                        J Offline
                        Jon Rista
                        wrote on last edited by
                        #17

                        No problem. I think I kind of confused the issue anyway by using the term "packaging". :P I've been delving deep into AOP with the PostSharp framework lately, so my perspective on things is a bit skewed from the norm right now. From the AOP perspective, aspects kind of referr to usages. If full AOP capabilities existed in .NET, you could "package up" aspects and apply them to multiple points in your code using external means. This allows a more appropriate separation of concerns, allowing you to neatly isolate cross-cutting concerns from core concerns. PostSharp doesn't yet allow such flexibility in applying aspects, but its getting close.

                        L 1 Reply Last reply
                        0
                        • J Jon Rista

                          No problem. I think I kind of confused the issue anyway by using the term "packaging". :P I've been delving deep into AOP with the PostSharp framework lately, so my perspective on things is a bit skewed from the norm right now. From the AOP perspective, aspects kind of referr to usages. If full AOP capabilities existed in .NET, you could "package up" aspects and apply them to multiple points in your code using external means. This allows a more appropriate separation of concerns, allowing you to neatly isolate cross-cutting concerns from core concerns. PostSharp doesn't yet allow such flexibility in applying aspects, but its getting close.

                          L Offline
                          L Offline
                          led mike
                          wrote on last edited by
                          #18

                          That sounds cool. It was nice to talk with someone that can speak design for a change. Thanks. Have a great weekend :beer:

                          led mike

                          J 1 Reply Last reply
                          0
                          • L led mike

                            That sounds cool. It was nice to talk with someone that can speak design for a change. Thanks. Have a great weekend :beer:

                            led mike

                            J Offline
                            J Offline
                            Jon Rista
                            wrote on last edited by
                            #19

                            led mike wrote:

                            It was nice to talk with someone that can speak design for a change. Thanks.

                            Agreed! :beer:

                            L 1 Reply Last reply
                            0
                            • J Jon Rista

                              led mike wrote:

                              It was nice to talk with someone that can speak design for a change. Thanks.

                              Agreed! :beer:

                              L Offline
                              L Offline
                              Leftyfarrell
                              wrote on last edited by
                              #20

                              Great discussion guys. Thanks for the comments and sorry I was away for a while... this tab got lost within 40 others in my Firefox window... For now, what we wound up doing is something like this: Global.Common assembly contains base classes for Validation. Logic to load validation error message strings is different depending on what "area" of the application you are calling validation from, ie. UI vs. BLL Each of our "areas" also has an assembly for common code (shared among the assemblies in that area) so we have: UI.Common and BLL.Common For the validation example... we created a validation class in UI.Common that inherits from Global.Common, but provides its own implementation that loads the message strings from our BLL by calling through WCF. We will create another validation class in the BLL.Common that inherits from Global.Common, but this implementation will call the BLL methods directly to load the message strings, without going through WCF. This way, the cross-cutting concerns are kept within the 3 Common assemblies and uses a provider model idea for the loading of message resources.

                              1 Reply Last reply
                              0
                              Reply
                              • Reply as topic
                              Log in to reply
                              • Oldest to Newest
                              • Newest to Oldest
                              • Most Votes


                              • Login

                              • Don't have an account? Register

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • World
                              • Users
                              • Groups