Entity Managers and Helpers - Guidance and Experience

Posts   
 
    
Posts: 34
Joined: 08-Nov-2007
# Posted on: 06-Jun-2008 03:17:30   

I gradually refactoring from having my service facade contain loads of BL, essentially near everything bar the DAL/LLBLGen.

I start moving stuff into managers however it feel unnatural because in there seems to be multiple concerns:

class CustomerManager { CustomerEntity GetCustomerById(string ID); CustomerEntity GetCustomerByName(string Name);

 UpdateLoyaltyPointsr(CustomerEntity customer)

}

In the first two methods I'm getting a customer Entity, in the third method I'm passing this back. Is this how managers should work, or should we have seperate managers (or suggest naming conventions) for wrappers and utilities

e.g.

class CustomerTaskManager { (ctor)CustomerTask (CustomerEntity entity)

   UpdateLoyaltyPointsr();

}

class CustomerManager { CustomerEntity GetCustomerById(string ID); CustomerEntity GetCustomerByName(string Name)
}

and then do

CustomerEntity selectedCustmer = customerManagerInst.GetCustomerByName("Frans"); CustomerTaskManager selectedCustomerTasks = new CustomerTaskManager(selectedCustomer); selectedCustomerTasks.UpdatedLoyaltyPoints();

Discuss disappointed

stefcl
User
Posts: 210
Joined: 23-Jun-2007
# Posted on: 10-Jun-2008 08:15:45   

Hello,

In my case, I have decided to create two different classes per entity. One that contains the typical CRUD stuff (CustomerManager) and an another with only business methods.

I started with only one class but it quickly became a bit bulky.

tprohas
User
Posts: 257
Joined: 23-Mar-2004
# Posted on: 13-Jun-2008 19:59:03   

Hello all,

I'll say that in my case I chose to do this all in one Manager class per subject. So I did something like the following.

CustomerManager() EmployeeManager() ProductManager() OrderManager() .CalculateShipping(OrderEntity order) .GetCustomerOrders(CustomerEntity customer)

Then for perisistance I did this.

PersistanceManager() .SaveEntity .SaveEntityCollection ...

I did choose the mix the business logic with the fetch logic in one class even though it did tend to make the classes big. I liked being able to get at everything related to a subject in one place.

tprohas
User
Posts: 257
Joined: 23-Mar-2004
# Posted on: 16-Jun-2008 21:25:22   

I just want to add to my last message and say this.

I've now used this architecture on a number of small web applications. It seems to work well and I like working with it. I don't however know how good a design it really is. The largest web application I've used it with was the Wrench Science web site (http://www.wrenchscience.com/). This site receives around 2000 unique visits a day with a very small number of employees using the web admin site for manager the business. So far there have not been any performance issues with it.

That being said I would love to get the opinions of other who know more about software architecture then I do on the followign design.

I generally break my architecture into the following assemblies.

Company.Division.LLBLGen Company.Division.LLBLGenDBSpecific

I then using the above assemblies in my business logic layer assembly.

Company.Division.AppServices

The AppService assembly then contains the following (simplistic example).

ServiceManager The ServiceManager class contains static methods for creating instances of manger classes.

PersistanceManager The PersistanceManager class contains instance methods for saving, deleting and any other persistance related logic.

ProductManger The Product Manger class contains instance methods for fetching entities and performing business logic related to the entities.

Here is an example of actual code.

ServiceManager


public class ServiceManager
{
    public ServiceManager()
    {
        //
    }

    public static PersistanceManager GetPersistanceManager()
    {
        string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();
        return new PersistanceManager(connectionString);
    }

    public static ProductManager GetProductManager()
    {
        string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();
        return new ProductManager(connectionString);
    }
}

PersistanceManager


public class PersistanceManager
{
    private string connectionString = string.Empty;

    public PersistanceManager(string connectionString)
    {
        this.connectionString = connectionString;
    }

    #region public bool SaveEntity(IEntity2 entity)
    /// <summary>
    /// 
    /// </summary>
    /// <param name="entity"></param>
    /// <returns></returns>
    public bool SaveEntity(IEntity2 entity)
    {
        bool saved = false;

        using (DataAccessAdapter adapter = new DataAccessAdapter(this.connectionString))
        {
            saved = adapter.SaveEntity(entity);
        }

        return saved;
    }
    #endregion

    #region public int SaveEntityCollection(IEntityCollection2 collection, bool refetch, bool recursive)
    /// <summary>
    /// 
    /// </summary>
    /// <param name="collection"></param>
    /// <param name="refetch"></param>
    /// <param name="recursive"></param>
    /// <returns></returns>
    public int SaveEntityCollection(IEntityCollection2 collection, bool refetch, bool recursive)
    {
        int affectedRows = 0;

        using (DataAccessAdapter adapter = new DataAccessAdapter(this.connectionString))
        {
            affectedRows = adapter.SaveEntityCollection(collection, refetch, recursive);
        }

        return affectedRows;
    }
    #endregion

    #region public bool SaveEntity(IEntity2 entity, bool refetchAfterSave)
    /// <summary>
    /// 
    /// </summary>
    /// <param name="entity"></param>
    /// <param name="refetchAfterSave"></param>
    /// <returns></returns>
    public bool SaveEntity(IEntity2 entity, bool refetchAfterSave)
    {
        bool saved = false;

        using (DataAccessAdapter adapter = new DataAccessAdapter(this.connectionString))
        {
            saved = adapter.SaveEntity(entity, refetchAfterSave);
        }

        return saved;
    }
    #endregion

    #region public int UnitOfWorkCommit(UnitOfWork2 uow, bool autoCommit)
    /// <summary>
    /// 
    /// </summary>
    /// <param name="uow"></param>
    /// <param name="autoCommit"></param>
    /// <returns></returns>
    public int UnitOfWorkCommit(UnitOfWork2 uow, bool autoCommit)
    {
        int affectedRows = 0;

        using (DataAccessAdapter adapter = new DataAccessAdapter(this.connectionString))
        {
            affectedRows = uow.Commit(adapter, autoCommit);
        }

        return affectedRows;
    }
    #endregion
}

ProductManager


public class ProductManager
{
    private string connectionString = string.Empty;

    public ProductManager(string connectionString)
    {
        this.connectionString = connectionString;
    }

    public EntityCollection<ProductEntity> GetProducts()
    {
        EntityCollection<ProductEntity> results = new EntityCollection<ProductEntity>();

        SortExpression sort = new SortExpression();
        sort.Add(ProductFields.Name | SortOperator.Ascending);

        using (DataAccessAdapter adapter = new DataAccessAdapter(this.connectionString))
        {
            adapter.FetchEntityCollection(results, null, 0, sort);
        }

        return results;
    }

    public ProductEntity GetProduct(int productId)
    {
        ProductEntity entity = new ProductEntity(productId);

        using (DataAccessAdapter adapter = new DataAccessAdapter(this.connectionString))
        {
            adapter.FetchEntity(entity);
        }

        return entity;
    }
}

This can then be used in the following way.


EntityCollection<ProductEntity> products = ServiceManager.GetProductManager().GetProducts();

// Use entity collection for a data source.

ProductEntity product = ServiceManager.GetProductManager().GetProduct(1);

// Make changes to product.
product.Name = "My new name";

bool saved = ServiceManager.GetPersistanceManager().SaveEntity(product);

Any advice or comments on this would be greatly appreciated.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 25-Jun-2008 09:30:13   

There's one rule to know: there's no silver bullet. This means: if you ask someone if your design is good, that someone can't answer that question, simply because 'what's good?'. So if that someone answers the question still, it will be an answer which is based on what the person prefers.

If your design works great for you, if the code is maintainable, the users are happy, your design worked. If you see problems in these areas, change the design to fix these problems. That's more or less all there's to be said about this. One could say: "Use a webservice" or "Use remoting" or "use more tiers" but that's all not really adding anything valuable: most time of a project is spend on maintenance. If the maintenance of the code is easy, doable and practical, you've done it right. If the application works well, and the users are happy, why would there be a problem? I mean: if you would do the design differently and maintenance is also good, performance is OK and the users are happy, what's different? simple_smile

Frans Bouma | Lead developer LLBLGen Pro
tprohas
User
Posts: 257
Joined: 23-Mar-2004
# Posted on: 25-Jun-2008 09:34:28   

simple_smile

Thank you Otis. That's a good answer to me. I guess that means that there also isn't anything obviously wrong about it which makes me feel like I did my research well.

Enjoy