Best practices on N-Tier for Web/Windows

Thread is closed for posting
9 posts, 2 answers
  1. Schmidty
    Schmidty avatar
    22 posts
    Member since:
    Jan 2007

    Posted 18 Aug 2009 Link to this post

    This is my first time using OpenAccess and have found a lot of resources/links in my research so far. But I have a few questions I'd like to ask directly.

    I currently have two class libraries using OA, my model/domain project and a Repositories project. I'd like to retain this structure for both a Web & Windows application. Each application will have its own Business layer which I'd like to use these layers.

    Model/Domain- Has my generated classes from OA.
    Repositories - Contains repositories per generated class. Each has Get/Save/etc. methods. I'd like to try keep all ObjectScope management in this layer if possible.

    These are my two questions:

    1) My biggest question is how to deal with the IObjectScope. I'd ideally like to have the IObjectScope transaction run in these repositories like this:

    public Person GetPersonById(int personId) { 
       Person personnull
       using (IObjectScope scope = ObjectScopeProvider.GetNewObjectScope()) 
       { 
         scope.Transaction.Begin(); 
         person= (Person )scope.GetObjectById(Database.OID.ParseObjectId(typeof(Person ), personId.ToString())); 
       } 
       return person; 
     } 
     


    public void Save(Person person) {
      using (IObjectScope 
    scope = ObjectScopeProvider.GetNewObjectScope()) { 
        scope.Transaction.Begin(); 
        scope.Add(person); 
        scope.Transaction.Commit(); 
      }
    }

    Regarding the ObjectScopeProvider class, when should I use GetObjectScope() and when should I use GetNewObjectScope()? In my current setup, I might require a IObjectScope when I get an Person object & another when I save it. I'm sensing this is not the most efficient way which is why I'd like to see how I can optimize my setup.

    2) I've read that updates should be done within the IObjectScope's transaction. The process I have right now would have a Person class be created and populated in the Business layer and then persisted via a method such as PersonRepository.Save(). Will this work properly if the IObjectScope just adds the object to the scope? It worked for me but again I'm asking for the "best practice" on this.

    I hope I explained my questions clearly. I haven't seen any examples following a more domain driven design pattern. Any help/advice on how I can efficiently and effectively structure it is greatly appreciated.
  2. Answer
    IT-Als
    IT-Als avatar
    381 posts
    Member since:
    Sep 2008

    Posted 18 Aug 2009 Link to this post

    Hi Schmidty,

    I don't what layers you have above the Repository, but in our solution we have a business layer on top of the Repository.

    I see some problems in your current architecture, please correct me (you, moderators, any) if I am wrong.

    1)
    The object scope is only "alive" in your repository. Thus, if any method in business layer navigates a property of the Person class instance (returned from the Repository) that is not loaded (as per the lazy load / fecth plan).. OA will fail (trying to lazy load), because it can not "reconnect" to the object scope when it is disposed (in the Repository)

    2)
    The transaction demarcation is not well-defined. Let's say a business layer method operates on several Repositories and late in what I call the business process (the method in the business layer performing business logic) something fails...  How will you rollback those changes already committed??

    In our solution (services based on WCF hosted in II7) we have done it this way:

    We have the following layers:
    WCF service layer
    Business Layer
    Persistence Layer (Repository)

    Rule is that the Model (assembly containing persistent classes) i available to all layers, except the WCF service layer.

    The transaction (see it as a "business transaction") is started at a coarse grained level..namely in the business layer.. So whatever happens during possibly several calls down to the persistence layer, we can always rollback.
    Thus, the objectscope is retrieved per business transaction (one atomic unit of work), which is also the unit of work (method) that is exposed in our WCF service method.

    So, the a new object scope gets created for each request hitting the service layer.. and stored during that request in something we call a clientcontext.

    In the lower layers (business, repository, etc) the object scope is alive and residing in the client context, which can be retrieved there.

    Pseudo code for at business layer method would be something like:

    public void CreatePerson(Person person)
    {
      // Get the object scope from the client context
      using (ObjectScope scope = ContextManager.GetClientContext().ObjectScope)
      {
        // Start the "business transaction"
        scope.Transaction.Begin;
        try
        {
          // Do some business work, involving one or more Repositories
       
          // Commit the work
         scope.Transaction.Commit;
       }
       catch
       {
          // Oops, error - rollback
          scope.Transaction.Rollback();
          throw;
       }
      }
    }

    Does it make sense at all...?

    When asking yoursel where to intialize the objectscope...ask yourself where your transaction demarcation is..  Your atomic unit of work.

    Sorry, for company regulations and NDA I cannot post real source code.

    Regards

    Henrik

  3. Zoran
    Admin
    Zoran avatar
    534 posts

    Posted 18 Aug 2009 Link to this post

    Hi Mark,

    As Henrik suggested in his post, the object scope should be retrieved per business transaction(atomic unit of work) and saved in a client context so it can be reused during one request.
    You should check the following Knowledge-Base article where some of the best practices for management of the object scope are briefly discussed.

    Best wishes,
    Zoran
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  4. IT-Als
    IT-Als avatar
    381 posts
    Member since:
    Sep 2008

    Posted 18 Aug 2009 Link to this post

    Hi Zoran,

    I wasn't aware of this KB article. Thanks for sharing :-)

    We have been using (and continuesly refining) our WCF architecture around OA for about 4 years now and it is rock solid.

    Regards

    Henrik
  5. Schmidty
    Schmidty avatar
    22 posts
    Member since:
    Jan 2007

    Posted 18 Aug 2009 Link to this post

    Hi Henrik,

    Thank you for your response. I understand what you're saying, and just want to clarify some things that were discussed.

    1) In your example code, would the repository still have contain the update/retrieval/delete/query code? It seems that the only concern is that the business & persistence layers are working on the same scope, but I'm still unsure what the repository method would look like.

    The idea for me was to conduct the transaction in the business layer but use the repository just for DB access. How would a save method work that would need to begin/commit (as in my original example) if the transaction began in the business layer?

    2) So in the KB article, I could retrieve the scope in the business & repository layer using this method? IObjectScope scope = ScopeFactory.GetPerRequestScope(HttpContext.Current). For my Windows app, I did find a ScopeFactory example that should work for that though I would've liked to have just one persistence layer to use for both.

    As this is my first venture into OA, I'm starting to "get" how I need to alter some aspects of my applications. I'm anticipating using OA for a long time in other projects, and this is really helping me with my first steps with OpenAccess.

    Thanks everyone for your help so far.
  6. Answer
    IT-Als
    IT-Als avatar
    381 posts
    Member since:
    Sep 2008

    Posted 19 Aug 2009 Link to this post

    Hi Schmidty

    1)
    No, the transaction  need not to be restarted in the Repository layer. The only place where you Start, Commit and Rollback is in the business layer.Another thing is, even if you would like it to be done that way OA does not support nested transactions.

    You are right that the Factory (we call it the ContextManager) is accessible from both layers.. and the only task of it is to provide the ObjectScope bound to the currently executing request.

    2)
    So in your repository you just retrieve your scope from the ContextManager and you use the scope to perform the query. Like this, still in psuedo code:

    public void SavePerson(Person person)
    {
      ObjectScope scope = ContextManager.GetClientContext().ObjectScope
      // Perform add
      scope.Add(person);

      // Do not commit or rollback the transaction - it is handled in the upper business layer..
      // If something fails - throw exception for the business layer to handle (remember the try-catch block?)

      // Do NOT dispose the scope....  ContextManager will handle the object scope lifecycle
    }

    SavePerson is a specific case because the only thing needed is to add the object (person) to the scope. On Commit in the business layer it will actually get persisted in the database.

    Regards

    Henrik
  7. Schmidty
    Schmidty avatar
    22 posts
    Member since:
    Jan 2007

    Posted 19 Aug 2009 Link to this post

    Hi Henrik,

    I really appreciate your guidance/help on this. After not having to develop a DAL for quite some time & dealing other different ones I was just hitting a stumbling block with this.

    After your posts, I set up a test page using the new scope handling & it worked great. I feel fairly confident moving ahead with the web portion of things.

    This setup is actually pretty close to how I wanted it to work (with the business layer doing the bulk of the work). The pseudo code works great  as it's clear enough to adapt it to my domain model. I just got overwhelmed in trying to understand how to fit OA and the Object scope into the design I wanted.

    Just by chance, have you used OA for Window apps? If so, do you have any gotchas or best practices you might be able so share. I plan on using the scope factory for Winforms and will probably have additional questions when I get to that project.

    If you've using OA for about 4 years, that's the type of product I want to use & build on. I'm excited to start using this & you don't know how much of a help you've been.

    Schmidty
  8. IT-Als
    IT-Als avatar
    381 posts
    Member since:
    Sep 2008

    Posted 19 Aug 2009 Link to this post

    Hi again,

    I am only happy that I could help you. As you can see from the posts around on the forum, the issues around getting OA to fit in their architecture has really been a serious matter for other people, too.

    Unfortunately, I have not been developing Windows Forms apps with OA. In our system we use a CAB based smart client that "talks" to our WCF services. So OA is only server side.

    Regarding Windows Forms apps, the only thing I can think of is to have the ObjectScope initialized and disposed with the AppDomain of the application...and then simalary as with the ContextManager... provide the one (singleton) scope to other modules / part of the application when it is needed.

    Good luck with your project.

    Best regards

    Henrik
  9. Zoran
    Admin
    Zoran avatar
    534 posts

    Posted 19 Aug 2009 Link to this post

    Hello Schmidty,

    The best-practices approach for handling the object scope in a Windows Forms application is to keep the scope on a per thread-level. This is an approach that has provided successful with our clients so far. I think that the following KB article should give you the answers you are looking for.

    Regards,
    Zoran
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
Back to Top