How to use Domain Driven Design - value objects

Original: how to use domain-driven design - value objects

Outline

As a domain-driven design in a tactical mode, the most central part - the value of the object. Has been one of most developers are willing to try or you are using DDD's most mentioned concept. But in the learning process, you will be affected because the traditional development model, it is often difficult to apply the concept of the value of the object, and looked puzzled when the value of object persistence. This article will start from the concept of value of the object, explain what is the value of the object and how to use the value of the object, and gives the corresponding code fragment (code snippets in this tutorial are using C # , the latter part of the actual project is also based DotNet Core Platform ).

What is the value of the object

First, let's take a look at the original "domain-driven design: the core software complexity Road countermeasures" interpretation of the value of the object:

Many of the objects are not shown on the concept, they describe certain characteristics of a transaction.
It is used to describe certain aspects of the art and the concept itself is not represented by objects called Value Object (Object value).

At this point the author is this:
we are like this:

The authors then use the "address" of the concept expanded to everyone what is the value of an object, we should be how to find the value of the object. So you will find that many of the articles in DDD are using this example to explain to you. Of course there will be people who read the sobering feeling, but I like this dish of chicken, when feeling after use in addition to address this thing will give him the abstract, the other thing that ye scrawl how to write.

For Example :

    public class DemoClass
{
    public Address  Address { get; set; } 

    //…………
}

OK, now we have to carefully analyze and understand the value of the object, although the concept a bit abstract, but at least one key point that we can very clearly captures that value does not identify the object, that is to say this thing called the Value Object he did not ID. It is also crucial, he conveniently behind our in-depth understanding of the value of the object.
Since the value of the object is a thing without an ID (something), then we have to think about a thing we do not need to identify under what circumstances by ID:

  • "When grocery shopping: I have five dollars, you have five dollars" here would care about my money and your money is the same one, the same code, the same combination (a five, five one )? Obviously not. Because their value is the same, to buy something, it is so it does not require ID's.

  • "Toilet time: while there are two vacancies, are the same toilet, clean all the same." Here you will care about you on the toilet which is a production specification, which encodes it? Obviously not, you only care about whether it is structurally sound, can be used. Of course, some people might say: "When I was in the toilet, every time I look for a number on the first row of the first toilet." Well, think, when within a very urgent, you will consider this issue ? Although this example cited a little wonderful, but it is worth our reflection, in the process of developing some of the things we found (class), is it really need an identity ID.

By the above two examples, I believe a thing you do not ID the identity (class) has left little impression on inside your head. So let us look at one of our cases provided for in the original:

  • When a child's painting, he noted that the color and the thickness of the brush tip. But if the two have the same color and thickness of the brush, he may not care which one to use. If you have lost a pen, he can come up with a new set from a pen to continue to paint the same color pen, I will not care about has changed a pen.

Value is based on the object context

Please note that this is a very important prerequisite. You will find in the above three cases, there is a similar prefix: "??? time." In other words, we consider the value of the object when it is based on the actual environmental conditions and contextual factors (context) of. This problem is very easy to understand: for example, you are a child's father, when you're inside the home, the children have heard called the "father", even if you do not see your children, you know that my father refers to your own; when you are on the subway, I suddenly heard a cry "Daddy" from the next compartment, you do not think this is calling you. Therefore, when implementing the field-driven, all elements are considered based on the context, all out of context the value of the object is not useful .

The value of the current context object may be another entity context

Entity is equally important concept in tactical mode, but now we have not discussed before, we just need to understand the entity is a transaction ID has on the line. That one and the same thing in the current environment may not have a unique identifier, but it may require a special ID in a different environment to identify it. Consider the example above:

  • The same five dollars lower at this time in a monetary production environment. It will consider this same five dollars if a heavy number, apparently is not allowed to re-number the currency issue. So each one currency must have a unique identity as a judge.

  • Similarly toilet, this time in a property management environment. It will consider the factory encoding the toilet, the toilet if fails, it will be returned for repair, and tracked through a unique id.

Apparently, the same thing, actually has a different meaning in different contexts.

How to use value objects

At this point, you should be according to your own environment and where context (context) capture the value of your own objects, and such money ah, ah name, color it and so on. Let's consider how to put it in the actual code.

The first example of the value of an object five dollars to as an illustration, this time we shop at the supermarket context, we might have captured down the value of an object called "money" (Money) is. According to the wording of our past, to have a look at what kind of code:


public class MySupmarketShopping
{
    public decimal Money { get; set; } 

    public int MoneyCurrency { get; set;}
}

Avoid the use of primitive types

A closer look at the code above, you will find that this is no problem that way, quite rightly indicated. I was in the supermarket shopping, I have the money through a property to show. It is also in line with the style we used to write the class.
Of course, this wording also does not mean it's wrong. Just say no better show the current environment we want to show things.
This logic may be very abstract, especially when we wrote the code for so many years, I had developed such a qualitative thinking. Well, consider one of the following questionnaire:

Sports questionnaire (1)  
Full name ________
gender ________ (string)
Week physical activity ________ (integer)
Common sports equipment ________ (integer)
Movement Questionnaire (2)  
Full name ________
gender ________ (male \ female)
Week physical activity ________(0~1000cal\1000-1000cal)
Common sports equipment ________ (treadmill \ dumbbells \ Other)

It should now be clear to understand the gist of it. From 1 sports watch, as if a sex outside, we did not know back empty need to express what it means, and the environmental movement is 2 plus the specific name and options, you can make people understand. If the movement is converted into a code that we are familiar with, whether similar to the above MySupmarketShopping category yet. The so-called primitive types, that is, we are familiar with (int, long, string, byte ............). The years of coding habits, so we think they show that things are more normal attributes of units, but as the answer to two questionnaires given, as this code is very confusing, at least give other people read your code causes some small obstacles.

Is the value of the object and may have cohesive behavior

Next is to realize that the value of the object of our Money's time above. This is a very common life in a scene, it is possible that we build out the value of an object like this:


class  Money
{
    public int Amount { get; set; }
    public Currency Currency { get; set; }

    public Money(int amount,Currency currency)
    {
        this.Amount = amount;
        this.Currency = currency;
    }
}

Money object We have also introduced an object called currency (Currency), which is also the value of the object, indicating the type of money.
Next we change our above MySupmarketShopping .


public class MySupmarketShopping
{
    public Money Amountofmoney { get; set; } 
}

You'll find our original MySupmarketShopping currency attribute class, by converting to a new value object to the money after the object. Because the concept is actually part of the currency of money, it should not be extracted in order to interfere with my shopping.

At this time, Money value object already has its rightful property, then it is so complete it?
Still thinking about a problem, perhaps my grocery shopping in a foreign country, I need my RMB converted into US dollars. That it is a behavior is encoded in our action, it may be a method. Then we will convert this way Where did I put it? To MySupmarketShopping ? Obviously, you will know if there is Money in the value of an object, then this behavior should not be converted to MySupmarketShopping , but belongs to Money . Then Money class was granted in order to expand like this:


class  Money
{
    public int Amount { get; set; }
    public Currency Currency { get; set; }

    public Money(int amount,Currency currency)
    {
        this.Amount = amount;
        this.Currency = currency;
    }

    public Money ConvertToRmb(){
        int covertAmount = Amount / 6.18;
        return new Money(covertAmount,rmbCurrency);
    }
}

Note: After completion of this behavior, we return to a new Money object, rather than make changes on the current object. This is because we value objects have a very important feature, immutability .

Value objects are immutable: once created, the value of the object will never be changed. On the contrary, any attempt to change its value, the result should be to create a whole new instance with expectations.

Look at an example

In fact, we in the usual encoding process, some type is a typical value object, but we did not have this concept of a complete system to be discovered.
For example, in .NET, DateTime class is a classic example. Some programming languages, his primitive types fact, there is no date type this argument, such as Go language by introducing time implementation of the package.
Try, if not DateTime class to represent how would you date this concept, and how to achieve mutual conversion (for example between the date DateTime provided AddDays , AddHours and other methods).

This is a case of a real project, maybe you can pass it to deepen the impression that the concept of the value of the object in your mind.

Demand is the case: the part of a period of time deducted, and returns the number of hours remaining. For example there is a time period 12:00 - 14:00 another time period 13:00 - 14:00. Returns the number of hours 1.
// Fragment 1

    string StartTime_ = Convert.ToDateTime(item["StartTime"]).ToString("HH:mm");
    string EndTime_ = Convert.ToDateTime(item["EndTime"]).ToString("HH:mm");
    string CurrentStart_ = Convert.ToString(item["CurrentStart"]);
    string CurrentEnd_ = Convert.ToString(item["CurrentEnd"]);
    //计算开始时间
    string[] s = StartTime_.Split(':');
    double sHour = double.Parse(s[0]);
    double sMin = double.Parse(s[1]);
    //计算结束时间
    string[] e = EndTime_.Split(':');
    double eHour = double.Parse(e[0]);
    double eMin = double.Parse(e[1]);

    DateTime startDate_ = hDay.AddHours(sHour).AddMinutes(sMin);
    DateTime endDate_ = hDay.AddHours(eHour).AddMinutes(eMin);

    TimeSpan ts = new TimeSpan();
    if (StartDate <= startDate_ && EndDate >= endDate_)
    {
        ts = endDate_ - startDate_;
    }
    else if (StartDate <= startDate_ && EndDate >= startDate_ && EndDate < endDate_)
    {
        ts = EndDate - startDate_;
    }
    else if (StartDate > startDate_ && StartDate <= endDate_ && EndDate >= endDate_)
    {
        ts = endDate_ - StartDate;
    }
    else if (StartDate > startDate_ && StartDate < endDate_ && EndDate > startDate_ && EndDate < endDate_)
    {
        ts = EndDate - StartDate;
    }
    if (OverTimeUnit == "minute")
    {
        Duration_ = Duration_ > ts.TotalMinutes ? Duration_ - ts.TotalMinutes : 0;
    }
    else if (OverTimeUnit == "hour")
    {
        Duration_ = Duration_ > ts.TotalMinutes ? Duration_ - ts.TotalMinutes : 0;
    }

// Fragment 2

    DateTimeRange oneRange = new DateTimeRange(oneTime,towTime);
    DateTimeRange otherRange = new DateTimeRange(oneTime,towTime);
    var resultHours = oneRange.GetRangeHours() - oneRange.GetAlphalRange(otherRange);

First, look at the code fragment 1, a conventional way to achieve this function. But there is a large number of primitive types used to describe the problem, and the readability of the code amount is very complicated.
Next is the code fragment 2, in the realization of this process, we first try to find the common problem model, and therefore the value of the extracted object called time period (DateTimeRange) class out, and given the value of the object due behavior and attributes.


//展示了DateTimeRange代码的部分内容
public class DateTimeRange
{
    private DateTime _startTime;
    public DateTime StartTime
    {
        get { return _startTime; }
    }

    private DateTime _endTime;
    public DateTime EndTime
    {
        get { return _endTime; }
    }

    public DateTimeRange GetAlphalRange(DateTimeRange timeRange)
    {
        DateTimeRange reslut = null;

        DateTime bStartTime = _startTime;
        DateTime oEndTime = _endTime;
        DateTime sStartTime = timeRange.StartTime;
        DateTime eEndTime = timeRange.EndTime;

        if (bStartTime < eEndTime && oEndTime > sStartTime)
        {
            // 一定有重叠部分
            DateTime sTime = sStartTime >= bStartTime ? sStartTime : bStartTime;
            DateTime eTime = oEndTime >= eEndTime ? eEndTime : oEndTime;

            reslut = new DateTimeRange(sTime, eTime);
        }
        return reslut;
    }
}

By the value of the object to find out, and rich value of the object's behavior. Brought a lot of benefits for our code.

Value of the object persistence

About the value of the object persistence problem it has been a very difficult problem. Here we provide currently the two most common realization of ideas and methods for reference. And the method are for traditional relational databases. (Because of the nature Nosql, so no need to consider these issues)

The value of the field in the object mapping table

This method is also Microsoft's official program provided Eshop case, the inherent physical form of the type provided by EFCore to the value stored in the object field dependent entity table. Specific details reference may EShop achieve the object value . By this method, we persisted and finally out of the comparison result similar to this:

The value of the object as a separate table to store

In this way when the value of persistent objects stored as a separate table, and dependent for their primary object ID primary key. Formed dependent objects associated with acquired when a Join manner.
You may persist out the results like this:

It may not be the perfect way to persist

As the subtitle, it is currently possible and no perfect way for a persistent relational database persistence value of the object. The way a manner likely to cause a lot of redundant data, after all, the value of the object, as long as the value is the same as we think they are equal. If there is an address value is the value of the object "Sichuan", then there 100w users are in Sichuan, then we will save the contents 100w times. For some text information objects larger value, this may be too much loss of memory and performance. And acquired by EFCore be mapped object also has a problem, it's hard to get the value of an object down a combination of relationship, such as the value of the object A object B has a value, the value of the object B, the object has a value C. This may be a normal thing for modeling the value of objects, but when making maps indeed very difficult.
For the second approach, the presence of a large number of modeling objects of value, they are all we have to establish a persistent data in the table to keep them, so resulting in an increase in unlimited database tables, and accustomed to database-driven development staff, this could be a nightmare when trying to restore business relations through a database this is a very difficult task.
In short, then again, there still is not a perfect solution, you can only be to circumvent the above problems by their own conditions and experience, so as to achieve a compromise effect.

to sum up

Summary is probably not learned it. If you have time to continue to expand to other key concepts (entities, warehousing, field service, factories, etc.) of the article tactical mode.

Guess you like

Origin www.cnblogs.com/lonelyxmas/p/12065787.html