The core of this question is removing a Static Class smell from my Web-Api/MVC solution. It presently works but feels kind of dirty.
Present Solution
Standard solution with 6 projects:
- MVC
- Api
- Api.Client - wrapped client to make in-code calls
- Api.Model - Dto's, Request models, etc.
- Domain - Entity layer, services, client mocks
- Logging
In my domain layer I have a simple Asset.
using Constants.Taxonomy1;
public class Asset : TrackedEntity, IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[StringLength(128, MinimumLength = 1)]
[Required]
[Index("IX_NameTypeAndParent", 2, IsUnique = true)]
public string Name { get; set; }
[Required]
[Index("IX_NameTypeAndParent", 3, IsUnique = true)]
public AssetType Type { get; set; }
[Index("IX_NameTypeAndParent", 1, IsUnique = true)]
public Guid? AssetId { get; set; }
public virtual Asset Parent { get; set; }
public virtual ICollection<Asset> Children { get; set; }
}
A Guid, a name and an enum AssetType
. The AssetType defines what other types of Asset can be its parent or children. This behaviour is currently defined in an external library; the Constants.Taxonomy1
import. This is separate from the solution so that it can be reused elsewhere and contains mappings between the various Taxonomies that we used.
The Taxonomy, in its cut down version, looks like this:
namespace Constants.Taxonomy1
{
using System.Collections.Generic;
public enum AssetType : int
{
Undefined,
WTG,
WindFarm,
MeteorologicalStation
}
public static class Taxonomy
{
private static Dictionary<AssetType, List<AssetType>> childRelationships = new Dictionary<AssetType, List<AssetType>>()
{
{ AssetType.Undefined, new List<AssetType> { } },
{ AssetType.WTG, new List<AssetType> { } },
{ AssetType.WindFarm, new List<AssetType> { AssetType.WTG, AssetType.MeteorologicalStation } },
{ AssetType.MeteorologicalStation, new List<AssetType> { } }
};
private static Dictionary<AssetType, AssetType> parentRelationships = new Dictionary<AssetType, AssetType>()
{
{ AssetType.Undefined, AssetType.Undefined },
{ AssetType.WTG, AssetType.WindFarm },
{ AssetType.WindFarm, AssetType.Undefined },
{ AssetType.MeteorologicalStation, AssetType.WindFarm }
};
public static AssetType Parent(AssetType assetType)
{
return Taxonomy.parentRelationships[assetType];
}
public static List<AssetType> Children(AssetType assetType)
{
return Taxonomy.childRelationships[assetType];
}
}
}
Pretty simple, an enum of the asset type, a dictionary of what its parent is allowed to be and one for allowable children types. Retrieving the information is through a call to Taxonomy.Parent(AssetType type)
or Taxonomy.Children(AssetType type)
.
Different Taxonomy rules are placed in their own namespaces in this library.
- Constants.Basic
- Constants.Taxonomy1
- Constants.Taxonomy2
- Constants.FarTooMany
Each of which have an enum
AssetType
, Taxonomy.Parent(...)
and Taxonomy.Children(...)
, so the Taxonomy rules can be switched by changing which namespace is loaded.
The Smell
OK, so this works but... I end up having to import Constants.xxxx
whenever I need the Enumeration type or want to check the heritage of an Asset. This leads to the library being used liberally through the solution, MVC, Api, Domain, Client and Model layers. It feels wrong.
Ideally the enumeration type and taxonomy behaviour should be defined down in the Domain, eliminating the need to call static methods and the shotgun spread of using Constants.xxxx
in the other projects.
Options?
I've tried out several options yesterday including:
Creating my own Generic type which wraps the
enum
and Taxonomy behaviour; but this can't be stored within the Asset entity as it's a complex type.Creating an Interface that the Entity should implement, this led to a lot of dead-end solutions, including injecting an implementation of the interface into the Entity constructor through Autofac, which knackers up the repository pattern being used to manage the entities. This falls foul of the "defining behaviours on Entities" Holy War which made for some "Duck and Cover" reading.
Considering a Singleton to hold the Taxonomy rules, which allows it be defined in the Domain layer, but retains the drawbacks of being static.
Potential Solution
I'm currently thinking that the Asset Type needs to be resolved in the services layer through which the entities are manipulated. The Asset would do away with the enumeration field and hold just a plain Integer.
The service layer on picking up the Asset will take the integer value and use this with a Taxonomy service to determine the Asset type and Parent/Child restrictions.
The Taxonomy service could then take the IAssetType interface (implementation defined in Autofac) in order to apply the desired Taxonomy behaviour to the Asset?
But, then again, I lose the convenience of being able to use an Enum when writing the MVC and Web-Api sections of the Solution.
Final Plea
As in the heading above, any help much appreciated.