- Home
- LLBLGen Pro
- Architecture
Petshop Architecture Discussion
Joined: 24-Jun-2005
I've been studying Microsoft's .NET Petshop app that Frans ported earlier this month to use LLBLGen, but have not seen too much discussion on the good and bad points of it, so I wanted to start a post about it.
I'll start the discussion with a few observations, for what they are worth.
I think it does a reasonably good job of separating the various layers (PL, BL, and DAL). Having these three tiers, one of which is completely generated by LLBLGen, makes it easy to understand why designing a system in this manner is beneficial from a development standpoint. The BL can be reused by any number of client interfaces, without having to worry that a new client will miss implementing a rule that could compromise the data in the system. It also ensures a standard methodology for accessing the data and returning it to the client.
With an exception or two, Petshop uses a manager pattern, which I know a number of people here use and find pretty flexible and easy to implement. This design pattern makes it pretty easy to follow, even for people new to OOP and application layers.
Data is passed between the layers using LLBLGen entities, and collections. I know the purists advise against doing this because it more closely ties the layers to the underlying DB structure, and while that is true, it makes passing data much easier and requires significantly less code than would otherwise be necessary.
The original Petshop app by Sun was based on an MVC pattern, which is a popular pattern used in the Java world, but I'm not sure how much it is used in the .NET world. Does having a manager pattern instead of an MVC pattern hurt, or in someway hinder, the design/extensibility of the Microsoft port of Petshop to .NET?
The use of the EntityValidator interface ensures that anywhere within the BL where a call is made to the DAL to save data, the data will not be committed if a business rule is violated. This makes for a more consistent approach to enforcing the business rules, rather than trying to create your own methodology.
There are many places where exceptions are thrown, rather than using some management class to pass the error up to the calling layer and allow the UI to give the user a more definitive idea of what caused the error. I have sometimes struggled with the best way to handle errors. My rule of thumb is to only pass exceptions when the error is truly unknown, and unexpected. Otherwise, I have used combinations of returning bool's, int's (row counts), and even an error manager which allows the BL to pass more detailed information back to the PL for feedback to the user.
As I mentioned, I like the structure and organization of the port that Frans did and thank him for that, but wonder what other changes (if any) should be taken into consideration for it to truly be considered a great example of system design.
Joined: 17-Aug-2003
Scotty wrote:
Data is passed between the layers using LLBLGen entities, and collections. I know the purists advise against doing this because it more closely ties the layers to the underlying DB structure, and while that is true, it makes passing data much easier and requires significantly less code than would otherwise be necessary.
Everything comes with a price. Not having entity classes in the top tiers, requires you to do extra work, no matter what you chose in the lower layers. I'm not sure why people opt for extra work, but whatever the reasons are, not opting for entity classes in the upper tiers isn't without a price.
The original Petshop app by Sun was based on an MVC pattern, which is a popular pattern used in the Java world, but I'm not sure how much it is used in the .NET world. Does having a manager pattern instead of an MVC pattern hurt, or in someway hinder, the design/extensibility of the Microsoft port of Petshop to .NET?
MVC is something different than the Manager pattern. MVC is a way to de-couple object from viewer code. So you have an object ('Model' or 'M') or object graph, with data. To utilize that object graph in a gui, you use a 'Viewer' ('V'). To tie the viewer to the model, you use a controller ('C'). So the pattern is used for GUI purposes and offers you a way to tie any structure of objects to any viewer mechanism, as it's controlled by the controller.
MVC requires extra code and the conversion (projection of graph onto viewer and vice versa) can be slower than when you tie your data directly to the gui code.
The manager pattern is something which lives below that: it controls the implementation of BL rules in manager classes which thus solely handle object graphs and apply rules on them.
The use of the EntityValidator interface ensures that anywhere within the BL where a call is made to the DAL to save data, the data will not be committed if a business rule is violated. This makes for a more consistent approach to enforcing the business rules, rather than trying to create your own methodology.
Though EntityValidator classes can't be used for all BL rules: the cross-entity BL rules are often better off in a Manager class. For example, when a pet is purchased, the inventory is managed as well, which is done by hte inventory manager class, you shouldn't do that from a smaller class, as that logic doesn't belong there (IMHO).
There are many places where exceptions are thrown, rather than using some management class to pass the error up to the calling layer and allow the UI to give the user a more definitive idea of what caused the error. I have sometimes struggled with the best way to handle errors. My rule of thumb is to only pass exceptions when the error is truly unknown, and unexpected. Otherwise, I have used combinations of returning bool's, int's (row counts), and even an error manager which allows the BL to pass more detailed information back to the PL for feedback to the user.
What to do with exceptions is still up to debate and there are people who say: any error is an exception, and others say: exceptions are exceptional and not the same as errors. I.o.w: do you use exceptions for flow control or for error recovery after an exceptional state?
I always classify errors in 2 categories: 1) user error 2) system error
User errors are caused by anything that followed after a user action: input data, events etc. These errors therefore should be reported back to the user in a readable fashion. I never use exceptions for these. System errors are caused by actions which aren't the user's fault: configuration errors, bugs etc. I always use exceptions for these. In 99.99% of the cases, it's hard to recover from these. An application should have a way to catch these exceptions and view something to the user, in such a way that the exception doesn't make the app unstable. So for example a generic exception handler page is IMHO sufficient.
As I mentioned, I like the structure and organization of the port that Frans did and thank him for that, but wonder what other changes (if any) should be taken into consideration for it to truly be considered a great example of system design.
The main goal was to show how to use LLBLGen Pro in an ASP.NET application and to illustrate that the usage of the code doesn't differ from winforms code.
In the middle of the project I realized that the petshop application was not the best way to illustrate system design, as MS' port is horrible. I tried to re-work some of the code to be more friendly and more solid, though it should be reworked from the ground up actually.
I also left the ASP.NET code somewhat in tact, because it shouldn't be used as an example how to write ASP.NET GUI code. I have re-written parts of the ASP.NET client though, as the MS petshop code uses a horrible 'paging' repeater control which simply pages over data in-memory (as in: all pets in the DB). This is of course stupid, as with a database which contains thousands of products, this will burn down to the ground. So my code pages on the server (and I then also could show the paging functionality ). I also removed any ASP.NET caching code, as it was very obscure and made the code-behind pages pretty hard to read. (the MS port was clearly setup to be as fast as possible, and they'd paid every price for that to get there).
The only MVC still there is the cart viewer, which IMHO is too scattered around: the manager for the cart is in the BL, though it has some ties to the PL controller code for the shopping cart. The shopping cart manager code should be split up somehow.
Joined: 24-Jun-2005
Otis wrote:
Everything comes with a price. Not having entity classes in the top tiers, requires you to do extra work, no matter what you chose in the lower layers. I'm not sure why people opt for extra work, but whatever the reasons are, not opting for entity classes in the upper tiers isn't without a price.
I definitely agree. The classes are there so make use of them.
MVC is something different than the Manager pattern. MVC is a way to de-couple object from viewer code. So you have an object ('Model' or 'M') or object graph, with data. To utilize that object graph in a gui, you use a 'Viewer' ('V'). To tie the viewer to the model, you use a controller ('C'). So the pattern is used for GUI purposes and offers you a way to tie any structure of objects to any viewer mechanism, as it's controlled by the controller.
MVC requires extra code and the conversion (projection of graph onto viewer and vice versa) can be slower than when you tie your data directly to the gui code.
The manager pattern is something which lives below that: it controls the implementation of BL rules in manager classes which thus solely handle object graphs and apply rules on them.
As with anything else, I guess it depends on the requirements of the application you are developing as to whether using MVC is necessary
Though EntityValidator classes can't be used for all BL rules: the cross-entity BL rules are often better off in a Manager class. For example, when a pet is purchased, the inventory is managed as well, which is done by hte inventory manager class, you shouldn't do that from a smaller class, as that logic doesn't belong there (IMHO).
I definitely agree that cross entity validation is easier in the Manager class. EntityValidator is great for single entity validation.
What to do with exceptions is still up to debate and there are people who say: any error is an exception, and others say: exceptions are exceptional and not the same as errors. I.o.w: do you use exceptions for flow control or for error recovery after an exceptional state?
I always classify errors in 2 categories: 1) user error 2) system error
User errors are caused by anything that followed after a user action: input data, events etc. These errors therefore should be reported back to the user in a readable fashion. I never use exceptions for these. System errors are caused by actions which aren't the user's fault: configuration errors, bugs etc. I always use exceptions for these. In 99.99% of the cases, it's hard to recover from these. An application should have a way to catch these exceptions and view something to the user, in such a way that the exception doesn't make the app unstable. So for example a generic exception handler page is IMHO sufficient.
An error management class seems to work pretty well for me. Much the same way entities are shared among the tiers, I do the same thing with the error manager. That way, the PL can decide how best to let the user know what problem has occurred.
The main goal was to show how to use LLBLGen Pro in an ASP.NET application and to illustrate that the usage of the code doesn't differ from winforms code.
In the middle of the project I realized that the petshop application was not the best way to illustrate system design, as MS' port is horrible. I tried to re-work some of the code to be more friendly and more solid, though it should be reworked from the ground up actually.
I also left the ASP.NET code somewhat in tact, because it shouldn't be used as an example how to write ASP.NET GUI code. I have re-written parts of the ASP.NET client though, as the MS petshop code uses a horrible 'paging' repeater control which simply pages over data in-memory (as in: all pets in the DB). This is of course stupid, as with a database which contains thousands of products, this will burn down to the ground. So my code pages on the server (and I then also could show the paging functionality ). I also removed any ASP.NET caching code, as it was very obscure and made the code-behind pages pretty hard to read. (the MS port was clearly setup to be as fast as possible, and they'd paid every price for that to get there).
The only MVC still there is the cart viewer, which IMHO is too scattered around: the manager for the cart is in the BL, though it has some ties to the PL controller code for the shopping cart. The shopping cart manager code should be split up somehow.
I think your port was very beneficial in giving an example of how an application could be developed. To be honest, I did not even look at the PL. I was more interested in the interface developed for the PL in the BL, and how the BL communicated with the DAL, the plumbing so to speak
Scotty