Architecture to shield 3rd party from LLBLGen

Posts   
 
    
clint
User
Posts: 145
Joined: 15-Nov-2005
# Posted on: 01-Mar-2007 18:19:48   

I believe the answers to my questions are spread all around the forum, but I'm hoping I can consolidate all that information into a reply to this post.

First off we are using version 2.0 and using the adapter template with subclasses (although I wonder if I should skip the subclasses and put my custom code in partial classes instead.)

We plan on having a third party implement the user interface for one of our programs that use our database. It will be a web interface. We also will be making other programs that use the same database. These other programs may be using a web interface or a windows interface. So can anyone suggest an architecture and/or existing templates we could use so we can isolate a third party from LLBLGen? I don't want the third party to have to learn anything about using LLBLgen.

Ideally, I'd rather just have a third party use classes in some business layer above LLBLGen that would take care of creating, retrieving, updating, and deleting entities as well as calling other business processes. But I have a feeling this would involve writing a lot of code that may duplicate a lot of the LLBLGen functionality.

I was thinking about using Martin Fowler's Notification pattern. http://www.martinfowler.com/eaaDev/Notification.html. The GUI basically calls class methods in the business layer (he calls it domain layer). Data is passed to a class method in a Data Transfer Object. The Data Transfer Object contains both the data needed by a class method and a class for holding errors returned by that class method.

Some of my main concerns are about validation.

Although the database enforces a lot basic validation, I'd like to avoid the user seeing cryptic database errors like 'violated unique key constraint IX_OrderNumber on table Order'. I'd rather display an error that says something like 'That order number is already in use.'.

I noticed LLBLGen will throw exceptions if data is too big for a field. Although those errors aren't that cryptic, I'd like to replace them with an even friendlier error message. Also I'd like to avoid an exception being thrown.

In the Microsoft documents I read on exception handling, they urged throwing exceptions only for exceptional situations, not for common errors. They also mention doing validation checks to avoid exceptions being thrown in the first place. Which is one reason I was interested in using Martin Fowler's Notification pattern.

One way to avoid field size errors is to put a textlimit on the textbox in the GUI. Does anyone have some cool ideas on how the third party could set textlimits in the GUI without hardcoding or accessing LLBLGen code?

Thanks.

PilotBob
User
Posts: 105
Joined: 29-Jul-2005
# Posted on: 01-Mar-2007 19:10:43   

clint wrote:

Ideally, I'd rather just have a third party use classes in some business layer above LLBLGen that would take care of creating, retrieving, updating, and deleting entities as well as calling other business processes. But I have a feeling this would involve writing a lot of code that may duplicate a lot of the LLBLGen functionality.

I would say that you should create DAO objects which allow the UI programer to fetch your LLBLGen entities and also to Save/Update/Delete them.

BOb

clint
User
Posts: 145
Joined: 15-Nov-2005
# Posted on: 01-Mar-2007 19:52:23   

I would say that you should create DAO objects which allow the UI programmer to fetch your LLBLGen entities and also to Save/Update/Delete them.

Thanks for the reply Bob.

I'm assuming DAO means Data Access Objects.

Let me see if I understand your suggestion.

The UI programmer would still be using the LLBLGen entities, but instead of using the DataAccessAdapter to get them, they would call some (manager?) class of mine that would return LLBLBen entities. By doing that, the UI programmer doesn't need to know anything about using the DataAccessAdapter. That's one learning curve out of the way.

They could then pass those entities to manager class functions for adding, updating, and deleteing.

OK.

Since the UI programmer would be setting fields on LLBLGen entities, I wonder if they need to know anything special about LLBLGen validation. I've only read the section about validation in the LLBLGen help file, I haven't tried anything yet. So I don't know if I have any issues to discuss at this point in time.

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 01-Mar-2007 22:10:18   

you can define your validators within the entity itself or override the createvalidator function to define your validation rules. (within the entity and validator class the overridden functions have the same interface).

all the validation functions are designed to fire at the appropiate time. when the adapter calls save/delete/fetch the proper validators are triggered.The GUI programmers don't have to do anything in terms of defining/calling the validation process.

Now, if they want to use ASP.Net validation then the GUI developers will have to define them. To dynamically assign ASP.Net validation will require either template changes, or heavy coding on your part. But if the GUI developers use the entity validation technique then they just catch ORMEntityValidtionExceptions and return the error message to the screen.

There is 1 validation method Validate() (or something like that) that does not fire automatically. This function is designed for the developer to call at any time. so if you wanted to expose this type of functionality your manager object would need a public function to call Entity.Validate(); or they could call Entity.Validate()/Entity.Validator.Validate() directly in the GUI depending on how the GUI developers interact with LLBL objects.

Since there are events designed for before save, before delete, and after load I haven't found a need for Validate().

PilotBob
User
Posts: 105
Joined: 29-Jul-2005
# Posted on: 03-Mar-2007 00:09:59   

clint wrote:

I'm assuming DAO means Data Access Objects.

Yes.

clint wrote:

The UI programmer would still be using the LLBLGen entities, but instead of using the DataAccessAdapter to get them, they would call some (manager?) class of mine that would return LLBLBen entities. By doing that, the UI programmer doesn't need to know anything about using the DataAccessAdapter. That's one learning curve out of the way.

Correct.

clint wrote:

They could then pass those entities to manager class functions for adding, updating, and deleteing.

They would pass to same DAO objects. You could create a baseDAO object to abstract those functions. No need for additional/seperate manager classes.

clint wrote:

Since the UI programmer would be setting fields on LLBLGen entities, I wonder if they need to know anything special about LLBLGen validation. I've only read the section about validation in the LLBLGen help file, I haven't tried anything yet. So I don't know if I have any issues to discuss at this point in time.

Not much. Basically LLBLGen validation at the field level doesn't allow the value to go into the field. At the entity level you are throwing an exception... you can create an exception like BusinessRuleViloationException and throw it.

This way the UI dev only needs to know very little about LLBLGen entities/collections. But, even if you created your own Business Layer / Domain Layer they would have to learn that and you would write more code.

So, the UI guys code might be as simple as:

CustomerDAO custDao = new CustomerDAO();
CustomerEntity customer = custDao.GetCustomerById(_id);
// Bind customer entity or whatever.
// Maybe call a method on the customer entity
try
{
   custDao.Save(customer);
}
catch (BusinessRuleViolation ex)
{
   // code to get exception error message or the validation errors from IDataErrorInfo interface on the entity.
}

you could also put the DAO type code right into your entities too... This gives and API sort of like:

CustomerEntity customer = CustomerEntity.GetCustomerById(_id);
// do stuff with entity

CustomerEntity.Save(customer); 
or:
EntityHelper.Save(customer);

They would also need to know how to use entity collections, generic or not, whatever.

BOb

sgay
User
Posts: 53
Joined: 23-Nov-2006
# Posted on: 03-Apr-2007 15:41:22   

I'm basically trying to achieve the same result.

I'm looking into replacing a home-made data layer by an ORM-based layer. The whole application relies on true POCOs (plus some plumbing to work around limitations induced by POCOs, such as figuring out which fields have changed...).

I'm fine with replacing them with anything else, such as LLBLGen generated "entities", but this has to be transparent to the team in charge of the (ASP.NET) UI.

nHibernate is very good at pretending it manages POCOs (until you get bitten by the fact that it only pretends to manage POCOs and that proxies can have surprising behaviors), but I'd rather use LLBLGen, for... reasons that are off-topic here.

Assuming that LLBLgen will let me use 'Person' or 'Address' and not 'PersonEntity' or 'AddressEntity' class names, the UI team would still load/save objects via the helper objects they use today:

Person person = Persons.GetById(33);
person.Name = "foo";
Persons.Save(person);

IList<Car> cars = Cars.GetAllCars();
...

And these objects will proxy the calls to LLBLGen, so this means zero changes to the UI code.

My next issue is that the entities expose far too much of LLBLGen internals. Intellisense lists plenty of things I'd like them to ignore--basically, all I'd like them to see is the POCO properties: Person.Id, Person.Name, enough. That might change in the future, but for now it is important.

Assuming that writing a layer that would map LLBLGen entities to POCOs to LLBGen entities is not an option, what would be the best solution (if any) to achieve this?

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 03-Apr-2007 16:16:37   

if you want the GUI developers to know nothing of LLBL then you will need to have a transition layer in place.

You're currently using a propriatary DAL/BLL, correct? The PAL talks to the objects in the BLL. For LLBL entities to be transparent you'll need to create some type of transistion layer. This could be done using a wrapper object, or pass DTO type objects bewteen the PAL and BLL.

Either way someone will have major coding changes. Be it the PAL developers who will begin using LLBL entities instead of your custom entities. Or you BLL developers who need to design a middle tier for the PAL to communicate with the LBLL entities.

If it's a small project I would have the PAL devs learn LLBL. If it's a larger project I would opt for the BLL devs to create wrappers/DTOs for PAL/BLL communication.

here is post about auto-generating DTOs to mirror LLBL entities http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=6261 this may be the solution you need for PAL/BLL communication. The DTOs are generice enough that minimal code changes would be required if the Model/DAL changed.

sgay
User
Posts: 53
Joined: 23-Nov-2006
# Posted on: 03-Apr-2007 17:47:19   

jmeckley wrote:

You're currently using a propriatary DAL/BLL, correct? The PAL talks to the objects in the BLL. For LLBL entities to be transparent you'll need to create some type of transistion layer. This could be done using a wrapper object, or pass DTO type objects bewteen the PAL and BLL.

We are using a proprietary DAL with a clean interface (GetPerson, SavePerson, etc). It seems that I could transition to a LLBL layer "simply" by rewriting the DAL so that is relies on LLBL to Get/Save/etc. if I accept that the UI devs see the LLBL entities directly, with all their properties.

What I'd like is to achieve the same result without exposing all the properties to the UI devs.

Vis. the DTOs you point to: - They seem to handle fields but not relations? We do need objects graphs. - I am not sure how to map the DTOs back to LLBL entities.

Wrappers seem more appropriate as I would not lose the connection to the original entity?

Obviously I still need to think further into this flushed

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 04-Apr-2007 10:56:06   

You could opt for adapter, which is designed for this. Adapter entities do expose properties etc. but don't allow persistence logic through the entities, you need an adapter for that. As that adapter access is only available in your BL tier, the UI can't do any persistence, it has to call the BL tier for all data.

Your UI developers then only reference the dbgeneric dll, not the dbspecific dll.

Frans Bouma | Lead developer LLBLGen Pro
sgay
User
Posts: 53
Joined: 23-Nov-2006
# Posted on: 04-Apr-2007 13:26:05   

Otis wrote:

You could opt for adapter, which is designed for this. Adapter entities do expose properties etc. but don't allow persistence logic through the entities, you need an adapter for that.

I had already opted for adapter. That way, the UI devs can not have access to persistence logic. However, they are not shielded from things such as User.SetNewFieldValue(), User.SetRelatedEntityProperty(), etc.

I'd like to avoid telling them (i.e. documenting) that a User has a User.Id, a User.Name, and please ignore User.AnythingElse() because you should not really see it.

Not that they can do harmful things with them. And these functions may be required for databinding to work properly. But, to come back to my original point, I'm only trying to replace an existing DAL without changing the way the UI devs work.

Another example: currently, a User has a User.Group but no User.GroupId. As far as I know (but I know little) LLBL will want this GroupId property. I can live with it. But I'd really like to get rid of User.FieldsCustomPropertiesOfType.

Do I make sense? confused

PilotBob
User
Posts: 105
Joined: 29-Jul-2005
# Posted on: 04-Apr-2007 20:03:40   

sgay wrote:

I'd like to avoid telling them (i.e. documenting) that a User has a User.Id, a User.Name, and please ignore User.AnythingElse() because you should not really see it.

You can hide stuff from the debugger... or it can be labled an "advanced" property. However, since most of these members are in ORMSupport you don't have access to attribute them.

I'm thinking you could create your own BaseEntity class that sits between your Entities and EntityBase. In this class you could attribute all of those public methods. However, my guess would be that not EVERY public member in EntityBase is virtual.

Perhaps Frans would consider marking the non persistent fields that are really there for LLBLGen as Advanced... though I doubt it.

This is another place where using POCO's really comes in handy. But, I think you are on the right track... use DTO's or create an interface for each entity that only contained the persisted fields and implement then explicitly. YOu could probably add these to your Entity templates farily easily.

BOb