When it comes to layers and tiers, keep it simple. Some developers get very academic when it comes to business tiers but all they are doing is adding unnecessary complexity (beware architecture astronauts). So my first advice is to keep it simple and easy to maintain for your particular application.
The goal is to hit a balance between maintenance complexity and flexibility. You want your app to have the flexibility to grow without requiring a lot of changes but at the same time, you want to be able to have an application that is simple to understand.
Having at least one layer between your persistence and client is a good thing. This gives you the flexibility of putting a domain and/or service in-between the client and the database. I wouldn't go beyond this unless you are solving a specific problem.
As for loading from your persistence, you can use partial classes to extend the generated persistence classes. This is an easy way to add on additional functionality while keeping the original properties and methods in tact.
If you need to transform persistence objects to domain objects used by the application, I would recommend putting a constructor or static Create method on the domain objects that take a persistence object and then transforms it into a domain object. You can do the same thing going back to persistence.
using MyApp.Domain;
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
public int ID { get; set; }
public static MyApp.Domain.Customer Create( MyApp.Persistence.Customer pcustomer )
{
return new MyApp.Domain.Customer
{
Name = pcustomer.Name,
Address = pcustomer.Address,
ID = pcustomer.ID
}
}
public void Persist( MyApp.Persistence.Customer pcustomer )
{
pcustomer.ID = this.ID;
pcustomer.Name = this.Name;
pcustomer.Address = this.Address;
}
}
Here is what the partial class might look like. The key is to have the namespace of the class the same as the namespace of your generated persistence type.
using MyApp.Persistence;
public partial class Customer
{
public string FormatedAddress
{
// I now have access to all the generated members because
// I'm extending the definition of the generated class
get
{
return string.Format( "{0}\n{1},{2} {3}",
StreetAddress,
City,
State,
ZipCode );
}
}
}