I do the following:
DB layer - modified LLBLGen templates
BL Layer - Encapsulates calls to the DB, and provides validation for all business objects, this is where I expose typed lists, business processes, and validation.
GUI Layer - I created a framework of user controls that can have parent/child relation ships, this calls the BL and never the DB.
COM Layer - this exposes specific calls to the BL, or instatiates the GUI layer and presents it to the user. This layer handles any data translations needed for VB6 etc. I keep this interface the simplest possible.
In this way my normal 3 layers are all .Net designed and can be used from any of our .Net applications, and the COM exposes just the functionality I need to expose to VB6. This in my opinion is the key to it working, you can't expose a complex object through COM without taking some pretty big performance penalties.
I expose things in my COM layer like a fetch routine, that will instatiate the BL layer, call a retrieval method, massage any data if required and return it to VB6 for display in a VB6 grid.
Other examples include, long business processes as a simple call, or a method that causes a .Net form to appear just as if I had called it from .Net which encapsulates all the business logic and load/save functionalities where possible.
The COM stuff is really basic here is a coding example, I removed most of the logic and put in a comment placeholder but it should be enough to give you the idea, this piece gets put in a file called SHIP.COM.Leads:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using SHIP.Framework;
using SHIP.Framework.Exceptions;
using SHIP.Framework.GUI;
using SHIP.Framework.Security;
using SHIP.BL.Leads.SalesmanMileage;
using SHIP.GUI.Leads.SalesmanMileage;
namespace SHIP.COM.Leads.Interop
{
/// <summary>
/// Provide an interface so we get intellisense in VB6
/// </summary>
[Guid("REPLACE WITH UNIQUE GUID")]
public interface IFormAddMileage
{
int LoadForm(int salesRepID, int leadID, DateTime apptDate);
int ShowForm(bool modal);
int UnloadForm();
int AddMileage(int salesRepID, int leadID, DateTime apptDate, double mileage);
}
[Guid("REPLACE WITH A SECOND UNIQUE GUID")]
[ClassInterface(ClassInterfaceType.None)]
public class AddMileageHandler : IFormAddMileage
{
// private vars here
public AddMileageHandler()
{
}
#region IFormAddMileage Members
public int LoadForm(int salesRepID, int leadID, DateTime apptDate)
{
int RetVal = 0;
try
{
// instantiate our GUI and BL here
RetVal = 1;
}
catch (Exception ex)
{
ExceptionManager.Publish(ex);
RetVal = -1;
}
return RetVal;
}
public int ShowForm(bool modal)
{
int RetVal = 0;
try
{
if (_AddMileageDialog != null)
{
if (modal)
{
_AddMileageDialog.ShowDialog();
}
else
{
_AddMileageDialog.Show();
}
RetVal = 1;
}
else
{
RetVal = -2;
}
}
catch (Exception ex)
{
ExceptionManager.Publish(ex);
RetVal = -1;
}
return RetVal;
}
public int UnloadForm()
{
int RetVal = 0;
try
{
if (_USRAddMileage != null)
{
_USRAddMileage.Dispose();
}
if (_AddMileageDialog != null)
{
_AddMileageDialog.Dispose();
}
RetVal = 1;
}
catch (Exception ex)
{
ExceptionManager.Publish(ex);
RetVal = -1;
}
finally
{
_USRAddMileage = null;
_AddMileageDialog = null;
_Manager = null;
}
return RetVal;
}
public int AddMileage(int salesRepID, int leadID, DateTime apptDate, double mileage)
{
int RetVal = 0;
try
{
// instantiate the BL and do a process that would normally be in .net gui
if (_Manager.SaveSalesmanMileageExpenseBill())
RetVal = 1;
else
RetVal = -2;
}
catch (Exception ex)
{
ExceptionManager.Publish(ex);
RetVal = -1;
}
finally
{
_Manager = null;
}
return RetVal;
}
#endregion
}
}
The VB6 side of this looks like this:
Dim MileageForm As SHIP_COM_Leads_Interop.IFormAddMileage
Set MileageForm = New SHIP_COM_Leads_Interop.AddMileageHandler
Call MileageForm.LoadForm(CInt(TDBGrid1.Columns(eGrid.SalesRepID).Value), mlLeadID, mdApptDate)
Call MileageForm.ShowForm(True)
Call MileageForm.UnloadForm
and
Dim SalesmanMileage As IFormAddMileage
Set SalesmanMileage = New AddMileageHandler
SalesmanMileage.AddMileage SalesRepID, LeadID, ApptDate, Mileage
Set SalesmanMileage = Nothing
AddSalesmanMileage = True