1
\$\begingroup\$

I have the structure Corporation/Company/Facility/Storage and only Storage could be part of Facility, and Facility could be part of Company, and Company part of Corporation. My main problem is for the code to work I need to add the Corporation to itself.

public class BusinessEntityData
{
    public Guid ID { get; set; }

    public Guid ParentID { get; set; }

    public string Title { get; set; }
    public string Number { get; set; }

    public int Type { get; set; }
}

public static class Context
{
    public static List<BusinessEntityData> BusinessEntityList { get; set; } = new List<BusinessEntityData>();
}

This is how the data entity looks like and the context is for static list which I'm using for testing.

public interface IBusinessEntity
{
    string Title { get; set; }
    string Number { get; set; }

    int Type { get; set; }

    List<BusinessEntityData> GetChildren(string number);
    void AddBusinessEntity(IBusinessEntity entity);
    void Remove(IBusinessEntity entity);

    void Validate(IValidationVisitor visitor, BusinessEntityData data);
}

public interface IValidationVisitor
{
    void VisitCompany(BusinessEntityData data);
    void VisitFacility(BusinessEntityData data);
    void VisitCorporation(BusinessEntityData data);
    void VisitStorage(BusinessEntityData data);

}

This are both interfaces which I use for the VisitorPattern and Composite.

Here I implement the interface. Let's assume that the numbers are unique between all objects.

public abstract class AbstractBusinessEntity : IBusinessEntity
{

    public string Title { get; set; }
    public string Number { get; set; }

    public int Type { get; set; }

    public AbstractBusinessEntity(string number, string title)
    {
        Title = title;
        Number = number;
    }

    public void AddBusinessEntity(IBusinessEntity entity)
    {
        var currentElement = Context.BusinessEntityList.FirstOrDefault(x => x.Number == this.Number);

        var visitor = new ParentValidationVisitor();
        var data = new BusinessEntityData()
        {
            ID = Guid.NewGuid(),
            Title = entity.Title,
            Number = entity.Number,
            Type = entity.Type,
            ParentID = currentElement == null ? Guid.Empty : currentElement.ID
        };

        Validate(visitor, data);
        Context.BusinessEntityList.Add(data);
    }

    public List<BusinessEntityData> GetChildren(string number = null)
    {
        if (number == null)
            number = this.Number;

        List<BusinessEntityData> list = new List<BusinessEntityData>();
        var currentElement = Context.BusinessEntityList.FirstOrDefault(x => x.Number == number);

        if (currentElement == null)
            return new List<BusinessEntityData>();

        var children = Context.BusinessEntityList.Where(x => x.ParentID == currentElement.ID);

        foreach(var item in children)
        {
            list.Add(item);
            list.AddRange(GetChildren(item.Number));
        }

        return list;
    }

    public void Remove(IBusinessEntity entity)
    {
        var result = Context.BusinessEntityList.FirstOrDefault(x => x.Number == entity.Number);

        if (result == null)
            return;

        Context.BusinessEntityList.Remove(result);
    }

    public abstract void Validate(IValidationVisitor visitor, BusinessEntityData data);
}

My classes Facility, Corporation, Company and Storage are inheriting this AbstractBusinessEntity and only implement the Validate method in which I call separate visitor method.

This is how the visitor is looks like:

public class ValidationVisitor : IValidationVisitor
{
    public void VisitCorporation(BusinessEntityData data)
    {
        if (data.Type == (int)Helper.BusinessEntityType.Corporation)
            return;

        if (data.Type == (int)Helper.BusinessEntityType.Company)
            return;

        throw new BusinessEntityValidationException("You can add only Companies to Corporation");

    }
    public void VisitCompany(BusinessEntityData data)
    {
        if (data.Type == (int)Helper.BusinessEntityType.Facility)
            return;

        throw new BusinessEntityValidationException("You can add only Facility to Companies");
    }

    public void VisitFacility(BusinessEntityData data)
    {
        if (data.Type == (int)Helper.BusinessEntityType.Storage)
            return;

        throw new BusinessEntityValidationException("You can add only Storage to Facility");
    }

    public void VisitStorage(BusinessEntityData data)
    {
        throw new BusinessEntityValidationException("You can't add any business entity to Storage");
    }
}

Should the validation be in the visitor patter ?

Really strange to me is that when I run the program for this code to work properly I need to add Corporation to itself, to add the first element in the collection.

        Corporation corporation = new Corporation("Corporation", "Corporation TEst");
        corporation.AddBusinessEntity(corporation);

        var company1 = new Company("Company1", "Company1");
        var company2 = new Company("Company2", "Company2");
        var company3 = new Company("Company3", "Company3");
        corporation.AddBusinessEntity(company1);
        corporation.AddBusinessEntity(company2);
        corporation.AddBusinessEntity(company3);

I'm not sure if this is the proper way of doing it. Another way is the corporation to be out of the tree structure, but I'm not sure if this is correct as business logic. What could be a better approach ?

Here Github demo for full code of the example.

Here DotNetFiddle for full example. (not really good readability)

\$\endgroup\$
  • \$\begingroup\$ What's the point of having both BusinessEntityData and AbstractBusinessEntity ? \$\endgroup\$ – Bohdan Stupak Oct 29 at 11:05
  • \$\begingroup\$ @BohdanStupak I made it look like close to entity framework. BusinessEntityData will be the entity which will be saved into the database. The Abstract class defines how the logic will work in every class which is part of the tree structure. \$\endgroup\$ – mybirthname Oct 29 at 11:13
  • \$\begingroup\$ So if BusinessEntityData is solely for persistence this means that you Validate method is concerned with persistence rather than business logic. \$\endgroup\$ – Bohdan Stupak Oct 29 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.