I've been working on converting some legacy code from T-SQL stored procedures. My aim is to use DDD to make the transition. Below is an attempt to do this. Any comments on how well this is following DDD? Also any comments on the AreaGeneration domain service and its CalculateLocationsInArea would be most appreciated.
//consumer code
var locationRepository = new LocationRepository();
//areaGenerator is a domain service which is used to calculate locations between a from and to location
var areaGenerator = new AreaGenerator(locationRepository);
var areaParameters = new List<AreaParameter>
{
new AreaParameter("A001A", "A002B", AreaDirection.Column),
new AreaParameter("A001A", "A003C", AreaDirection.Row)
};
var locations = areaGenerator.CalculateLocationsInArea(areaParameters);
var area = Area.CreateArea("Test Area", "Special Area",areaParameters,locations);
public class Location
{
public string LocationCode { get; set; }
public int LocationID { get; set; }
public int Aisle { get; set; }
public int Column { get; set; }
public int Row { get; set; }
public string SortKey
{
get { return Aisle + "," + Column + "," + Row; }
}
}
public class AreaParameter
{
public AreaParameter(string fromLocation, string toLocation, AreaDirection areaDirection)
{
StartLocation = fromLocation;
EndLocation = toLocation;
AreaDirecton = areaDirection;
}
public string StartLocation { get; private set; }
public string EndLocation { get; private set; }
public AreaDirection AreaDirecton { get; private set; }
}
public class AreaGenerator
{
private readonly ILocationRepository _locationRepository;
public AreaGenerator(ILocationRepository locationRepository)
{
_locationRepository = locationRepository;
}
internal IEnumerable<Location> CalculateLocationsInArea(List<AreaParameter> areaParameters)
{
var locationsInArea = new List<Location>();
foreach (var areaDetail in areaParameters)
{
var startLocation = _locationRepository.GetLocationByCode(areaDetail.StartLocation);
var endLocation = _locationRepository.GetLocationByCode(areaDetail.EndLocation);
var startAisle = startLocation.Aisle;
var startColumn = startLocation.Column;
var startRow = startLocation.Row;
var endAisle = endLocation.Aisle;
var endColumn = endLocation.Column;
var endRow = endLocation.Row;
switch (areaDetail.AreaDirecton)
{
case AreaDirection.Row:
if (startAisle == endAisle)
{
Func<Location, bool> withinAisleAndColAndNotLastRow =
l =>
((l.Aisle == startAisle && l.Column >= startColumn && l.Row == startRow) &&
startRow != endRow)
||
((l.Aisle == startAisle && l.Column <= endColumn && l.Row == endRow) &&
startRow != endRow);
Func<Location, bool> withinAisleAndColAndIsLastRow =
l =>
((l.Aisle == startAisle && l.Column >= startColumn && l.Column <= endColumn &&
l.Row == startRow) && startRow == endRow)
||
((l.Aisle == startAisle && l.Column >= startColumn && l.Column <= endColumn &&
l.Row == endRow) && startRow == endRow);
Func<Location, bool> inBetweener =
l => l.Aisle == startAisle && l.Row > startRow && l.Row < endRow;
var results =
_locationRepository.GetLocations(
l =>
withinAisleAndColAndNotLastRow(l) || withinAisleAndColAndIsLastRow(l) ||
inBetweener(l));
locationsInArea.AddRange(results);
}
else
{
Func<Location, bool> withinAisleAndColAndIsLastRow =
l => (l.Aisle == startAisle && l.Column >= startColumn && l.Row == startRow)
|| (l.Aisle == endAisle && l.Column <= endColumn && l.Row == endRow);
Func<Location, bool> withinAisleAndColAndNotLastRow =
l =>
((l.Aisle == startAisle && l.Row > startRow) || (l.Aisle == endAisle && l.Row < endRow));
Func<Location, bool> inBetweener = l => l.Aisle > startAisle && l.Aisle < endAisle;
var results =
_locationRepository.GetLocations(
l =>
withinAisleAndColAndNotLastRow(l) || withinAisleAndColAndIsLastRow(l) ||
inBetweener(l));
locationsInArea.AddRange(results);
}
break;
case AreaDirection.Column:
Func<Location, bool> predicate1 =
l => ((l.Aisle == startAisle && l.Row >= startRow && l.Column == startColumn)
|| (l.Aisle > startAisle && l.Aisle < endAisle && l.Column == startColumn));
Func<Location, bool> predicate2 =
l =>
((l.Aisle >= startAisle & l.Aisle <= endAisle &&
l.Column > startColumn & l.Column < endColumn)
|| (l.Aisle == endAisle && l.Row <= endRow && l.Column == endColumn));
var locations = _locationRepository.GetLocations(l => predicate1(l) || predicate2(l));
locationsInArea.AddRange(locations);
break;
case AreaDirection.ALL:
var allLocations =
_locationRepository.GetLocations(
l =>
l.SortKey.CompareTo(startLocation.SortKey) >= 0 &&
l.SortKey.CompareTo(endLocation.SortKey) <= 0);
locationsInArea.AddRange(allLocations);
break;
default:
break;
}
}
return locationsInArea.OrderBy(l => l.Aisle).ThenBy(l => l.Column).ThenBy(l => l.Row);
}
}
public class Area
{
private string areaName;
private string areaDescription;
private readonly IList<Location> _locationsInArea;
private List<AreaParameter> _areaParameters;
private Area(string areaName, string areaDescription)
{
this.areaName = areaName;
this.areaDescription = areaDescription;
_locationsInArea = new List<Location>();
}
internal static Area CreateArea(string areaName, string areaDescription, List<AreaParameter> areaParameters,
IEnumerable<Location> locationsInArea)
{
var area = new Area(areaName, areaDescription);
area.AddAreaParameters(areaParameters);
area.AddLocationsToArea(locationsInArea);
return area;
}
private void AddLocationsToArea(IEnumerable<Location> locationsInArea)
{
foreach (var location in locationsInArea)
{
if (_locationsInArea.Any(l => l.LocationID == location.LocationID))
{
continue;
}
_locationsInArea.Add(location);
}
}
private void AddAreaParameters(List<AreaParameter> areaParameters)
{
_areaParameters = areaParameters;
}
}
public interface ILocationRepository
{
Location GetLocationByCode(string LocationCode);
IEnumerable<Location> GetLocations(Func<Location, bool> predicate);
}
public class LocationRepository : ILocationRepository
{
private readonly List<Location> _locations;
public LocationRepository()
{
_locations = new List<Location>
{
new Location {LocationCode = "A001A", LocationID = 1, Aisle = 1, Column = 1, Row = 1},
new Location {LocationCode = "A001B", LocationID = 2, Aisle = 1, Column = 1, Row = 2},
new Location {LocationCode = "A001C", LocationID = 3, Aisle = 1, Column = 1, Row = 3},
new Location {LocationCode = "A002A", LocationID = 4, Aisle = 1, Column = 2, Row = 1},
new Location {LocationCode = "A002B", LocationID = 5, Aisle = 1, Column = 2, Row = 2},
new Location {LocationCode = "A002C", LocationID = 6, Aisle = 1, Column = 2, Row = 3},
new Location {LocationCode = "A003A", LocationID = 7, Aisle = 1, Column = 3, Row = 1},
new Location {LocationCode = "A003B", LocationID = 8, Aisle = 1, Column = 3, Row = 2},
new Location {LocationCode = "A003C", LocationID = 9, Aisle = 1, Column = 3, Row = 3},
new Location {LocationCode = "A004A", LocationID = 10, Aisle = 1, Column = 4, Row = 1},
new Location {LocationCode = "A004B", LocationID = 11, Aisle = 1, Column = 4, Row = 2},
new Location {LocationCode = "A004C", LocationID = 12, Aisle = 1, Column = 4, Row = 3},
new Location {LocationCode = "B001A", LocationID = 13, Aisle = 2, Column = 1, Row = 1},
new Location {LocationCode = "B001B", LocationID = 14, Aisle = 2, Column = 1, Row = 2},
new Location {LocationCode = "B001C", LocationID = 15, Aisle = 2, Column = 1, Row = 3},
};
}
public Location GetLocationByCode(string LocationCode)
{
return _locations.FirstOrDefault(l => l.LocationCode == LocationCode);
}
public IEnumerable<Location> GetLocations(Func<Location, bool> predicate)
{
return _locations.Where(l => predicate(l));
}
}