Doug Chamberlain
Doug Chamberlain

Reputation: 11351

Is it possible to create an immutable property on a domain model?

We have a a number of logical domain objects that all reside in the same physical database table WorkStep. A simple sample of the metadata in the sql table is ID |NAME| TYPE, such that TYPE is a FK to a table that defines the types possible. However, once selected from the database each row might represent a different domain model, with VERY different business rules that must be applied. The business rule I am working with right now is that once created, the WorkStepType property one of these objects can NEVER change. SO, any call to that would cause this this to change would be a very bad thing.

So, is it possible to make an object that can be constructed via add-ons like automapper, and dapper, that can have a property set once. and never changed? Also, the types can not be cast to each other.

I haven't tried much, I'm not even sure it is possible to do this without hacky code

Upvotes: 0

Views: 214

Answers (2)

BlackjacketMack
BlackjacketMack

Reputation: 5692

Dapper maps to private properties (or private setters/mutators).

public class WorkStep
{
    public WorkStepType WorkStepType { get; private set;}
}

If you need the resulting domain object to be a specific type, then just make the above class a simple factory that generates the type you are looking for.

public class WorkStepFactory
    {
        public WorkStepType WorkStepType { get; private set;}

public T CreateWorkStep<T>() where T : WorkStepBase{...} 
    }

Upvotes: 1

David Sexton
David Sexton

Reputation: 9

I see this as two separate operations.

The first would be to return a list of WorkStepType objects available, the second would be to assign it as the immutable property on a "workflow" class exposing a generic type for your different objects.

Note - syntax is C#6. Not sure what the VB.NET equivalent would be.

Example

public class WorkStepType 
{
    // immutable properties
    public int Id { get; }
    public string Name { get; }

    public WorkStepType(int id, string name)
    {
        this.Id = id;
        this.Name = name;
    }
}

And the generic class as

public class Workflow<T>
{
    public WorkStepType WorkStep { get; }
    public T DomainModel { get; }

    public Workflow(WorkStepType type, T domainModel)
    {
        this.WorkStep = type;
        this.DomainModel = domainModel;
    }
}

How you choose to hydrate these objects is up to you. If you were using Drapper (built on top of Dapper) you might have a repository class that looked something like the below (although I'd advocate for separate CRUD repositories for WorkStepType/Workflows, this is to illustrate the idea)

public class WorkflowRepository
{
    // IDbCommander is a Drapper construct. 
    private readonly IDbCommander _commander;

    public WorkflowRepository(IDbCommander commander)
    {
        this._commander = commander;
    }

    public IEnumerable<WorkStepType> RetrieveAllWorkSteps()
    {
        // return all the available workstep types available 
        // to the user/application
        return _commander.Query<WorkStepType>();
    }

    public Workflow Retrieve(WorkStepType type)
    {
        // lookup the specific domain model for the supplied
        // workstep type. 
        var domainModel = _commander.Query<Workflow>(new { id = type.Id }).SingleOrDefault();
        return new Workflow(type, domainModel);
    }
}

This approach should give you the behaviour you're looking for and prove to be highly testable as well.

You may want to have the domain model publicly settable depending on whether you intend to assign a new instance to an existing WorkStepType. Not sure of your design/needs but I'm sure you get the gist.

Hope that helps.

Upvotes: 0

Related Questions