LLBLGen Examples to include a Business Logic Layer

Posts   
1  /  2
 
    
Fishy avatar
Fishy
User
Posts: 392
Joined: 15-Apr-2004
# Posted on: 22-Mar-2005 16:41:05   

jeffreygg wrote:

I tend to allow my BL layer to have both stateful and stateless properties. A common pattern you'll see in these forums is the Manager pattern. The idea is to dumb down the data objects themselves and place the business logic in classes on a per process basis. This has the benefit of a strong mapping of the functional requirements of the project to the technical implementation as well as providing for centralized access to common functionality.

Oh yea; examples. simple_smile Instead of having a rich SalesOrder object, you have a dumb one that is sent to (consumed by) a SalesOrderManager class. This is your BL class. It represents, perhaps, the taking of a new SalesOrder. But as the SalesOrder process spans many different entities (Orders, OrderDetails, Inventory, etc), you don't have to worry about where or on which entity the necessary methods are located; they're all located on the class that represents what you're doing: the SalesOrderManager class. Also, since one of the functional requirements is to be able to take Sales Orders, you know exactly where that functional requirement was technically implemented.

Anyway, I use stateful methods for functionality that directly represents the process I'm working on, and stateless methods for "service-like" functionality, ie, data fetches, etc.


BL.GetOrders (shared/static)
BL.GetCustomersByRegionID (shared/static)
BL.Save (instance)
BL.Validate (instance)

Hi Jeff,

First, thanks for all your posts, I've learned quite a bit from them.

I'm still having a little probplem wrapping my head around some of this. In the above quote you mention that SalesOrder is dumbed-down.

Does that mean that it only contains entities from different tables that are needed to support all the actions that take place in SalesOrderManager?

Are the calls to Get/Save/Delete located in SalesOrder but the validation, cordination, unitofwork and other things located in SalesOrderManager?

If you could expand on this, I would really appreciate it.

Thanks,

Fishy

jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 22-Mar-2005 22:48:54   

Fishy wrote:

I'm still having a little probplem wrapping my head around some of this. In the above quote you mention that SalesOrder is dumbed-down.

Does that mean that it only contains entities from different tables that are needed to support all the actions that take place in SalesOrderManager?

Are the calls to Get/Save/Delete located in SalesOrder but the validation, cordination, unitofwork and other things located in SalesOrderManager?

If you could expand on this, I would really appreciate it.

simple_smile

Think of the SalesOrderManager as encompassing all of the behavior of the SalesOrder process, and the SalesOrder as encompassing all of the data. So, the SalesOrder is merely the data representation of what you're working with, while you use the manager to perform actions against whichever SalesOrder object you send to it. Dumbed down = no behavior.

All fetches, business logic, validation, etc etc, is located in the manager class; the SalesOrder knows nothing of these things. The only things I might keep in the SalesOrder object is functionality narrowly explicit to the object. For examples, for an Address object, I might override the .ToString() method, or create a .Parse(addressText as string) method.

How you implement these principles is up to you. For example, the LLBLGen classes come with validator classes. This presumes that the entities themselves manage their own validation. However, since, using Adapter, I control the pipe all the way to the database, I choose to centrally control my validation in the manager classes; no entity gets saved without going through the manager classes anyway so I'm not worried about a validation getting missed.

Getting deeper, as the SalesOrderManager can be both stateful and stateless, I use the stateful aspect of the class to represent a single process occurring at any one time. Something like:


Dim salesOrder as New SalesOrderEntity(1)
Dim manager as New SalesOrderManager(salesOrder)
Dim results as ValidationResults

manager.DoStuff

results = manager.Save

If results.Count > 0 Then
DisplayValidationErrors(results)
End If


Some methods, like fetches of lookup data, etc I implement as shared/static methods:



Dim regionData as DataTable
regionData = SalesOrderManager.GetRegionData()
cboRegions.DataSource = regionData


But here's a key point: business logic is not inherently stateful; implementing business logic does not require one to maintain state. This means that I'm actually combining two distinct functions within one class (arguably a violation of the one class, one responsibility tenet): managing a GUI process, and implementing business logic. If I had more time in my current project, I might handle my GUI processes with a stateful process layer, and implement my business logic in a stateless BL layer. The nice things about keeping your BL stateless is that this can transition nicely into a services/web services architecture. Reduce the inputs and outputs of your remoted, stateless, BL layer to primitives and XML and you're starting to look like SOA. simple_smile

YMMV. By implementing both stateful and stateless properties in you manager classes, you can manage both process and business logic in one place. However, your business logic classes are now very closely bound to your presentation layer. This isn't the best for future reusability, but it works OK for smaller projects.

Jeff...

Fishy avatar
Fishy
User
Posts: 392
Joined: 15-Apr-2004
# Posted on: 23-Mar-2005 16:40:30   

Thanks Jeff,

Maybe I'm doing something similar. confused Currently I have a Manager class that keeps a private instance to all of the EntityCollections that are required for the Manager to perform a given set of functionally (ie. Place and Order with associated OrderLines). The Manager can also return a datatable or custom collection that would be used for the presentation layer.

The Logic for the Manager and the custom collections are stored in the same file.

First, what negatives do you see doing it this way?

Second, just to clarify. I take it that SalesOrder would contain all the EntityCollections and perhaps some methods to transform EntityCollections into custom collections or datatables. Also, I assume that the SalesOrder class is created using a Public Shared method from the Manager and that all CRUD and validation is done thru Shared Methods Also? Is that correct?

Thanks again,

Fishy

taylor74
User
Posts: 59
Joined: 06-Oct-2004
# Posted on: 24-Mar-2005 01:18:30   

davisg wrote:

I found an online updated, consolidated version of 'Modern Structured Analysis' called 'Just Enough Structured Analysis' by Edward Yourdon here:

http://www.yourdon.com/books/msa2e/

Hmm, looks like they took that down. It was working last week, but now I get the HTTP 404 - not found error.

jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 24-Mar-2005 02:58:10   

Fishy wrote:

Thanks Jeff,

Maybe I'm doing something similar. confused Currently I have a Manager class that keeps a private instance to all of the EntityCollections that are required for the Manager to perform a given set of functionally (ie. Place and Order with associated OrderLines). The Manager can also return a datatable or custom collection that would be used for the presentation layer.

The Logic for the Manager and the custom collections are stored in the same file.

First, what negatives do you see doing it this way?

That's basically what I do. The negatives are:

1) Your business logic is encapsulated in a class that is meant to handle and track one process at a time.

What if you want to perform a simple business function like adding an OrderLine to an order outside of the "normal" order taking procedure? With the current design you will have to go through the whole process to set state in the manager, call the appropriate method, then save the state. What if you wanted to centralize your business logic in a single physical tier on a separate machine and access it via web services? With the current design, you can't without serious refactoring as you don't want to use state with web services. The principle at work is the fact that the business logic required to manage business activities doesn't by necessity require state.

2) The business logic is bound to a class that is crafted to support a given UI layer.

I've found that by combining my UI state and my business logic into the same class, I inevitably craft the manager to directly reflect the needs of the specific UI layer. The problem comes when I have to change the UI layer - I have to change the manager as well, and I've lost the value of the abstraction. Plus, when designing that way you really haven't mapped the functional requirements into the business layer, you've mapped the presentation requirements which is a different beast.

3) You lose the abstraction of the business logic itself.

What if you wanted to change business logic depending on some context? If UI Process and Business Logic were kept in separate classes, the Strategy pattern becomes a very powerful tool for handling and delegating business logic functionality.

Second, just to clarify. I take it that SalesOrder would contain all the EntityCollections and perhaps some methods to transform EntityCollections into custom collections or datatables.

The problem is that the custom collections or datatables are highly UI specific. I don't want my entities cluttered up with methods to convert them into datatables for each presentation scenario. Instead, I choose to pull out most of my presentation collection data using dynamic lists, that are retrieved using reusable objects that define the parameters of the data. These "definition" objects are retrieved using a factory class and passed to methods that understand these definitions and return datatables.

Also, I assume that the SalesOrder class is created using a Public Shared method from the Manager and that all CRUD and validation is done thru Shared Methods Also? Is that correct?

That's definitely the optimal way of doing it, and again shows how we're structuring our manager classes to include both UI process state logic, and business logic.

Hope that helps..

Jeff...

Fishy avatar
Fishy
User
Posts: 392
Joined: 15-Apr-2004
# Posted on: 24-Mar-2005 03:59:25   

jeffreygg wrote:

Hope that helps..

Jeff...

Thanks Jeff simple_smile it does.

Take care,

Fishy smile

alexdresko
User
Posts: 336
Joined: 08-Jun-2004
# Posted on: 31-Mar-2005 18:13:27   

Wow! I hate that I'm coming into this conversation so late! Would have loved to participate from the begining.

Basically, I'm in the same boat as Davis. I'm quite comfortable with my programming skills, but having come from years of VBScript hell, OO concepts came late in the game. I've been improving greatly since the first release of VS.NET so I've got about that much experience under my belt.

Jeffery's points and suggestions are great! He answered a lot of the questions I came to this forum to ask today. My thinking was that I needed to build my entire BL by extending the code generated by LLBLGen. There's a lot I didn't like about that idea though. On the other hand, I'm having trouble figuring out how to best design a BL that takes advantage of LLBLGen's code so I don't have to duplicate anything. Passing up entities to the PL obviously makes sense. What I can't figure out is where to put my custom abstract data types that don't have anything to do with the DAL.

Like right now I want to create a "MassEmail" ADT. My first reaction was to create a new class called MassEmail in the <CompanyName>.DAL(LLBLGen starts here).EntityClasses namespace. My feeling is that if I do that, my MassEmail ADT should follow the same design patterns as the rest of that namespace... and that equals too much work.

On the other hand, if I created the MassEmail class in the <CompanyName>.BL namespace, I don't like the thought of mixing EntityClasses and my custom ADTs in the PL.

Hopefully someone can shed some light on that problem.

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 31-Mar-2005 20:26:12   

jeffreygg wrote:

Instead, I choose to pull out most of my presentation collection data using dynamic lists, that are retrieved using reusable objects that define the parameters of the data. These "definition" objects are retrieved using a factory class and passed to methods that understand these definitions and return datatables.

Jeffrey - I too am contemplating this whole abstraction system of thought and am right at this point in my dev, so I'm wondering what your "dynamic list" is typed as? I initially thought ICollection, then ArrayList, and now I'm not quite sure because neither of those seemed to do what I wanted.

Secondly, when you say that you retrieve these dynamic lists "using reusable objects that define the parameters of the data" what does that look like? (I'm not asking to see your code, just theorectically)

As a side note, I'm curious to see what Frans will produce as an example for this, assuming that is something he is still working on.

jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 31-Mar-2005 22:53:04   

Hey, guys simple_smile

alexdresko wrote:

<snip> ...

On the other hand, I'm having trouble figuring out how to best design a BL that takes advantage of LLBLGen's code so I don't have to duplicate anything. Passing up entities to the PL obviously makes sense. What I can't figure out is where to put my custom abstract data types that don't have anything to do with the DAL.

Like right now I want to create a "MassEmail" ADT. My first reaction was to create a new class called MassEmail in the <CompanyName>.DAL(LLBLGen starts here).EntityClasses namespace. My feeling is that if I do that, my MassEmail ADT should follow the same design patterns as the rest of that namespace... and that equals too much work.

On the other hand, if I created the MassEmail class in the <CompanyName>.BL namespace, I don't like the thought of mixing EntityClasses and my custom ADTs in the PL.

Hopefully someone can shed some light on that problem.

I would say depends on the approach. If you want intelligent objects, then extending the entity classes is a good idea. If you like centralized control over processes the Manager approach is better. In that case you would create a data representation of your MassEmail entity (whether in LLBLGen, or as a custom class) and write a manager class that consumes it.

Remember that LLBL stands for "Lower Level Business Layer". The entities generated are primarily data representations that happen to fit well into the Manager architecture, as the Manager architecture works primarily with dumb objects.

If you want to build smarter objects, then these objects will contain more of your business logic which logically places them into the Business Logic layer. I like to think of the LLBL-generated entities as data transports, or data representations of the true entity, rather than intelligent Domain-esque objects. If you extend them to include intelligence its a completely valid approach, just different than what we've been discussing.

NickD wrote:

Jeffrey - I too am contemplating this whole abstraction system of thought and am right at this point in my dev, so I'm wondering what your "dynamic list" is typed as? I initially thought ICollection, then ArrayList, and now I'm not quite sure because neither of those seemed to do what I wanted.

I'm actually using functionality defined in the LLBLGen framework called "Dynamic Lists". It's a method to retrieve sets of data type as datatables. It's very flexible and allows you access to most of the functionality of SQL aside from SQLs native functions. Check the docs for more info...

Secondly, when you say that you retrieve these dynamic lists "using reusable objects that define the parameters of the data" what does that look like? (I'm not asking to see your code, just theorectically)

I call them "DisplayCollectionDefinitions", and they are simply a container for the parameters of the set I wish to retrieve from the database:

Field List (including aggregated fields) Tables/Sources Relationships Order Grouping functions

These DisplayCollectionDefinitions are retrieved via a factory class such that I can standardize the presentation of various lists of data. If I need to present the data in a different way, I just add a new method and now I can always depend on having the data presented consistently in the new way.

When I need to display data I ask the factory for the appropriate definition and then pass it to my customized data layer which accepts them and returns a datatable. And, because of the wonderful reusability Frans brought to the underlying attributes (predicates, sort clauses, etc), filters on the data, sort clauses, etc can all be dynamically incorporated in to the final data request. Works very well.

simple_smile

Jeff...

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 01-Apr-2005 00:29:19   

jeffreygg wrote:

I'm actually using functionality defined in the LLBLGen framework called "Dynamic Lists". It's a method to retrieve sets of data type as datatables. It's very flexible and allows you access to most of the functionality of SQL aside from SQLs native functions. Check the docs for more info...

Jeff - I looked all over the docs and couldn't find them, which means I looked too hard. Help me out and point me to the right page. Thanks!

BTW - Your input is much appreciated

jeffreygg
User
Posts: 805
Joined: 26-Oct-2003
# Posted on: 01-Apr-2005 00:46:17   

NickD wrote:

jeffreygg wrote:

I'm actually using functionality defined in the LLBLGen framework called "Dynamic Lists". It's a method to retrieve sets of data type as datatables. It's very flexible and allows you access to most of the functionality of SQL aside from SQLs native functions. Check the docs for more info...

Jeff - I looked all over the docs and couldn't find them, which means I looked too hard. Help me out and point me to the right page. Thanks!

BTW - Your input is much appreciated

No problem. simple_smile The information regarding dynamic lists is contained in the "Using the typed view and typed list classes" within the respective scenarios (Self-Servicing and Adapter). The section with the information you're looking for is at the very bottom.

Also, see the section titled "Field Expressions and Aggregates" which gives more advanced usage of the dynamic lists functionality.

Jeff...

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 01-Apr-2005 01:23:23   

jeffreygg wrote:

No problem. simple_smile The information regarding dynamic lists is contained in the "Using the typed view and typed list classes" within the respective scenarios (Self-Servicing and Adapter). The section with the information you're looking for is at the very bottom.

Also, see the section titled "Field Expressions and Aggregates" which gives more advanced usage of the dynamic lists functionality.

Found it. I just didn't look close enough the first time.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39752
Joined: 17-Aug-2003
# Posted on: 18-Apr-2005 17:14:20   

The petshop example is now done, it's beta, and available with the rest of the beta stuff at: http://www.llblgen.com/pages/secure/upgrades.aspx

Frans Bouma | Lead developer LLBLGen Pro
1  /  2