Hi,
I don't know on what list I should post this message. If this is not the right
list, please excuse me.
This is how I'm trying to use DTOs to transfer data between layers in a
distributed system and I'm sure there are a lot of mistakes in it. Any ideas about it
are very welcomed. (The critical ones are too welcomed.)
The BusinessFacade contains only stateful components. The most important are:
1) AppEntryComponent. It lazily creates one instance of the other stateful components
in the BusinessFacade and keeps references to them. It's methods are basically
GetSecurityComponent, GetReadableCarDTOFactory, GetUpdatableCarDTOFactory.
2) SecurityComponent. This components holds user information and sensitive data, such
as the user's credentials.
3) ReadableCarDTOFactory : IReadableCarDTOFactory
4) UpdatableCarDTOFactory : IUpdatableCarDTOFactory
The domain objects are created using data access components in the Data Access
Layer. When ObjectSpaces is final, I'll probably use that framework for domain objects.
The domain data transfer objects implement syntactic validation in the writable
properties. That means syntactic validation occurs on the client side and on the
server side in the Data Access Layer. Semantic/business validation occurs on the
server side in the Business Rules Layer.
All the components are given a reference to the AppEntryComponent that created
them. That is because the stateful components need the other stateful components for
processing.
The DTO factory interfaces are implemented by Business Facade stateful components,
as well as by transactional components in the Data Access Layer and the updatable
interfaces are also implemented by transactional components in the Business Rules
Layer.
namespace SystemFrameworkProjects;
public interface IReadableCarDTOFactory
{
// mutable domain data transfer objects
CarDTO FindCarDTO(CarPK aCarPK);
ManufacturerDTO GetManufacturerDTOForCar(CarPK aCarPK);
// immutable custom data transfer objects
CarEngineDTO GetCarEngineDTO(CarPK aCarPK);
// immutable aggregate data transfer objects
CarAndManufacturerDTO GetCarAndManufacturerDTO(CarPK aCarPK);
}
public interface IUpdatableCarDTOFactory : IReadableCarDTOFactory
{
void UpdateCar(CarDTO aCarDTO);
void UpdateManufacturerForCar(CarPK aCarPK, ManufacturerDTO aManufacturerDTO);
}
namespace BusinessFacade;
// non-transactional, non-JITA
public class CarDTOFactory : ServicedComponent, IUpdatableCarDTOFactory // ,
IReadableCarDTOFactory
{
private SecurityComponent sc;
private DataAccessLayer.ReadableCarDTOFactory readableCarDTOFactory; // JITA, no
need to explicitly dispose of
private BusinessRulesLayer.UpdatableCarDTOFactory updatableCarDTOFactory; // JITA,
no need to explicitly dispose of
public void SetAppEntryComponent(AppEntryComponent comp)
{
// keep references to what we need
sc = comp.GetSecurityComponent();
readableCarDTOFactory= new BusinessRulesLayer.ReadableCarDTOFactory();
updatableCarDTOFactory = new BusinessRulesLayer.UpdatableCarDTOFactory();
}
public CarDTO IReadableCarDTOFactory.FindCarDTO(CarPK aCarPK)
{
if (sc == null)
throw new InvalidStateException("please call SetAppEntryComponent first");
sc.CheckFindCarPermission(aCarPK); // throws SecurityException
return readableCarDTOFactory.FindCarDTO(aCarPK);
}
public void IUpdatableCarDTOFactory.UpdateCar(CarDTO aCarDTO)
{
if (sc == null)
throw new InvalidStateException("please call SetAppEntryComponent first");
sc.CheckUpdateCarPermission(aCarDTO); // throws SecurityException
updatableCarDTOFactory.UpdateCar(aCarDTO);
}
}
The are 2 kinds of data access components in the DAL:
1) serviced components with Supported Transaction semantics for read-only data access
2) serviced components with Required Transaction semantics for updates
Example:
namespace DataAccessLayer;
[Transaction(TransactionOption.Supported)]
public class ReadableCarDAO : ServicedComponent, IReadableCarDTOFactory
{
...
}
[Transaction(TransactionOption.Required)]
public class UpdatableCarDAO : ReadableCarDAO, IUpdatableCarDTOFactory
{
...
}
namespace BusinessRulesLayer;
[Transaction(TransactionOption.Required)]
public class UpdatableCarDAO : ServicedComponent, IUpdatableCarDTOFactory
{
private DataAccessLayer.UpdatableCarDAO dao; // JITA, no need to be explicitly
disposed of
public UpdatableCarDAO()
{
dao = new DataAccessLayer.UpdatableCarDAO();
}
[AutoComplete]
public void IUpdatableCarDTOFactory.UpdateCar(CarDTO aCarDTO)
{
CarDTO oldCar = dao.FindCar(aCarDTO.PrimaryKey);
if (oldCar.WheelsCount != aCarDTO.WheelsCount) // enforce business rule
throw new WheelsCountMismatch();
else
dao.UpdateCar(aCarDTO);
}
}
The DAO components are coarse-grained and work only with domain data transfer
objects (similar to the entity beans in EJB 1.x based applications). They actually
implement something similar to the home interfaces in EJB. (It is possible simulate
the EJB local interfaces by optionally making the DAO components to be library
activated and making the fine-grained methods not deactivate the context at return but
I don't know if this approach is feasable with COM+.)
The DAO components are compile-time dependent only on domain data transfer
objects, but they do not depend on custom data trasfer objects.
The system will contain other business rules components, not closely related to
the DTO concept.
By this design I want to acomplish 3 things:
1) The client must not pass the username/password to the server at every call.
2) We can easily migrate domain objects from DAO to Object Spaces when it is final.
3) I keep most of the components stateless. The Business Facade Layer does not imply
so many scalability concerns like it is the Business Rules Layer or the Data Access
Layer, so the only layer of stateful components is the business facade.
Because of the DCOM "pinging" mechanism, in case the client crashes, the business
facade stateful components are automaticaly disposed of.
Is this design acceptable in the .NET/COM+ world ?
TIA,
Daniel Aioanei
You can read messages from the Advanced DOTNET archive, unsubscribe from Advanced
DOTNET, or
subscribe to other DevelopMentor lists at http://discuss.develop.com.