Introducción
Detrás de una gran interfaz siempre hay una gran clase, y en este caso no es una excepción, en general si la interfaz está bien definida en términos de herencia y parámetros de devolución no debería haber problemas.

Algunas recomendaciones:
- El objeto interno y en general todos los que se usen deben de ser privados, esto es obvio si permitiésemos que métodos externos modifiquen el objeto interno no tendría sentido usar Fluent Api, hay que revisar todos los modificadores de acceso a todos los objetos que se usen dentro de la clase.
- Solo puede haber un objeto interno raíz, es cierto que puede interesar tener una colección pero en ese caso es mejor tener un objeto que albergue una colección, tener un solo elemento raíz simplifica el desarrollo, por lo que en caso de querer tener una colección sería mejor crear un objeto raíz que englobe esta colección.
public class DatabaseBuilder :
IDatabaseBuilder,
IUserBuilder,
IRoleBuilder,
IRightBuilder,
IGroupBuilder,
ICredentialBuilder
{
...
}
- Los elementos no persisten hasta llamar al método sin contexto, cualquier uso de métodos modificará el objeto en memoría y no estará disponible a través de ningún método de acceso hasta que se llame al método sin contexto (Save(), Build()…) que es el que realmente creará el objeto o lo almacenará, si fuera acceso a una base de datos el commit iría en este punto, sino, es posible emular este efecto creando objetos o listas temporales.
En el ejemplo de muestra se usan variables privadas que almacenan partes del objeto que se usan en varias partes del método.
internal readonly Database Database = null;
private List<Right> rights = null;
private List<Login> logins = null;
private List<Group> groups = null;
private Role role = null;
private Login login = null;
private Group group = null;
- Crear varios ficheros, en este caso daría el consejo contrario a las interfaces, a no ser que sea poco código si hay varias interfaces es recomendable tener las implementaciones en ficheros diferentes para evitar mezclarlas y que se vea más claro el uso de cada clase.
- Vigilar la inicialización y construcción de objetos, usar Fluent Api no debería ser complicado y una buena inicialización de objetos en las clases superiores permite que estén disponibles para métodos anidados y no tengan que preocuparse de una creación condicional.
Si el código está correctamente relacionado llamar al método final sin contexto debería ser trivial.
Por ejemplo el método AddGroup crea no solo la lista de posibles grupos anidados sino también la lista de logins ya que al llamar a este método la única opción es llamar a métodos de la interfaz IUserBuilder que va a usar estos objetos.
public IUserBuilder AddGroup(string name = "")
{
groups = new List<Group>();
group = new Group();
group.Name = name;
logins = new List<Login>();
group.Logins = logins;
groups.Add(group);
Database.Groups.Add(groups);
return this;
}
- No usar constructores públicos, es mejor tener un método de factoría que devuelva la interfaz principal y sea esta la que se use durante la configuración, generalmente la creación del objeto por defecto suele ser necesaria y estos detalles deberían quedar ocultos.
En el ejemplo se usar un método estático que crea el objeto interno y devuelve la interfaz principal para poder continuar configurándolo.
private static IDatabaseBuilder databaseBuilder = null;
public static IDatabaseBuilder Create()
{
databaseBuilder = new DatabaseBuilder();
return databaseBuilder;
}
private DatabaseBuilder()
{
Database = new Database();
Database.Users = new List<Login>();
}
