Meta-Factories

A meta-factory is a factory that creates or encapsulates other factories. When implementing Traceability in a system, this pattern can also be very useful to reduce maintenance. The idea is that we create a single factory that will return an object that has all of the factories you need to create a complex class that contains additional classes. It can further be extended to return an object that contains all the factories needed to execute a service call.

One of the interesting effects of creating the meta-factory is that we are essentially creating a dependency graph showing every class we are using inside of the service call. This in turn, allows us to make a Traceable meta-factory that also ensures that every object being created is in fact Traceable.

An important aspect to consider for these meta-factories is how to handle DAOs. You may decide to pass in the external connections as constructor arguments, and have the meta-factory create the DAOs. Or, you may create the DAOs, and pass them into the meta-factory constructor. The latter approach allows for more flexibility, since you can add features to the DAOs. For example, in some cases you may want to add caching to a DAO that is not usually there. Or, in the case of the of the Traceable meta-factory, you may want to swap out the DAO based on if you want to allow updates to be sent to databases or other services, or if you want the DAO to add the output to the trace data but not actually update the databases (See Tool Design for more details).

As an example, we are going to create a meta factory for a service that uses two database connections, a service call, three functional classes, and a class that is a return type. Both database connections and the service call will be encapsulated in DAOs, which in turn will be used by other classes. Below is pseudocode for the constructors for the various classes. using Connectors; namespace ServiceName { namespace Daos { Database1(DatabaseConnection) Database2(DatabaseConnection) ExternalService1(ServiceConnection) } namespace Entities { Class1(Database1) Class2(Database2, ReturnedTypeFactory) Class3(ExternalService1, ReturnedTypeFactory) ReturnedType() } namespace Factories { Class1Factory(Database1) Class2Factory(Database2, ReturnedTypeFactory) Class3Factory(ExternalService1, ReturnedTypeFactory) ReturnedTypeFactory() } }

Next, we look at the Meta-Factory that will be used to construct all instances of Class1, Class2, and Class3. The process is rather straight forward: we use a static constructor to create all of the other factories, and make sure any interdependencies are taken care of. The three factories we need to use are then exposed. using Connectors; namespace ServiceName.Factories { public class MetaFactory { private Database1 _database1; private Database2 _database2; private Service1 _service1; private Class1Factory _factory1; private Class2Factory _factory2; private Class3Factory _factory3; private ReturnedTypeFactory _returned; public Class1Factory Class1Factory => _factory1; public Class2Factory Class2Factory => _factory2; public Class3Factory Class3Factory => _factory3; // this static constructor instantiates all of the factories // and makes sure that any dependencies are handled as well public static MetaFactory ConstructMetaFactory( DatabaseConnection database1, DatabaseConnection database2, ServiceConnection service1) { var db1 = new Database1(database1); var db2 = new Database2(database2); var svc1 = Service1(service1); var returned = new ReturnedTypeFactory(); var class1 = new Class1Factory(db1); var class2 = new Class2Factory(db2, returned); var class3 = new Class3Factory(svc1, returned); return new MetaFactory(db1, db2, svc1, class1, class2, class3, returned); } // this constructor takes one each of the factories and DAOs. It was made // protected so that the Traceable version may use it. protected MetaFactory(Database1 database1, Database2 database2, Service1 service1, Class1Factory factory1, Class2Factory factory2, Class3Factory factory3, ReturnedTypeFactory returned) { // this appears that we don't need several of the factories stored here // but there are advantages. For one, you can implement a function // that outputs all of the connection information used, or have the // meta-factory create a central structure that takes care of such needs _database1 = database1; _database2 = database2; _service1 = service1; _factory1 = factory1; _factory2 = factory2; _factory3 = factory3; _returned = returned; } } }

Next we look at the constructors of the Traceable versions of all the classes. They are the same as the Functional Layer, but they all take in the ITracer interface as well. Also note that their arguments, aside from the ITracer, are from the Functional Layer. using Connectors; using SD = ServiceName.Daos; using SE = ServiceName.Entities; using SF = ServiceName.Factories; using Tracer = Traceability.Tracer; namespace TraceableServiceName { namespace Daos { TraceDatabase1(ITracer, DatabaseConnection) : SD.Database1 TraceDatabase2(ITracer, DatabaseConnection) : SD.Database2 TraceExternalService1(ITracer, ServiceConnection) : SD.ExternalService1 } namespace Entities { TraceClass1(ITracer, SD.Database1) : SE.Class1 TraceClass2(ITracer, SD.Database2, SF.ReturnedTypeFactory) : SE.Class2 TraceClass3(ITracer, SD/ExternalService1, SF.ReturnedTypeFactory) : SE.Class3 TraceReturnedType(ITracer) : SE.ReturnedType } namespace Factories { TraceClass1Factory(ITracer, SD.Database1) : SF.Class1Factory TraceClass2Factory(ITracer, SD.Database2, SF.ReturnedTypeFactory) : SF.Class2Factory TraceClass3Factory(ITracer, SD.ExternalService1, SF.ReturnedTypeFactory) : SF.Class3Factory TraceReturnedTypeFactory(ITracer) : SF.ReturnedTypeFactory } }

Now we implement the Traceable Meta-Factory. Again we use a static factory method to construct the meta-factory. The arguments to the static constructor are the same as before, except we include an ITracer. using Connectors; using SD = ServiceName.Daos; using SE = ServiceName.Entities; using SF = ServiceName.Factories; using ITracer = Traceability.ITracer; using TD = TraceableServiceName.Daos; using TE = TraceableServiceName.Entities; using TF = TraceableServiceName.Factories; namespace TraceableServiceName.Factories { public class TraceMetaFactory : ServiceName.Factories.MetaFactory { private ITracer _tracer; // this static constructor instantiates all of the factories // and makes sure that any dependency are handled as well public static MetaFactory ConstructMetaFactory(ITracer tracer, DatabaseConnection database1, DatabaseConnection database2, ServiceConnection service1) { var db1 = new TraceDatabase1(tracer, database1); var db2 = new TraceDatabase2(tracer, database2); var svc1 = TraceService1(tracer, service1); var returned = new TraceReturnedTypeFactory(tracer); var class1 = new TraceClass1Factory(tracer, db1); var class2 = new TraceClass2Factory(tracer, db2, returned); var class3 = new TraceClass3Factory(tracer, svc1, returned); return new TraceMetaFactory(tracer, db1, db2, svc1, class1, class2, class3, returned); } // this constructor takes one each of the factories and DAOs. It was made // protected so that the Traceable version may use it. private TraceMetaFactory(ITracer tracer,Database1 database1, Database2 database2, Service1 service1, Class1Factory factory1, Class2Factory factory2, Class3Factory factory3, ReturnedTypeFactory returned) :base(database1, database2, service1, factory1, factory2, factory3, returned) { _tracer = tracer; } } }

Next: Code Generation

Copyright © 2017-2018 Adin H. Baber, all rights reserved.