When designing a domain, either way of following approaches can be used to access list of child entities inside a parent entity.

1. Get/Set

Following approach is straight forward and simple to implement, but anyone can overwrite the Orders collection.

public User 
{
    public IList<Order> Orders { get; set; }
}

or

2. Controlled Access

Following is bit complicated, however it provides it’s orders list controlled access.

public User 
{
    private IList<Order> _orders;

    public User()
    {
        _orders = new List<Order>();
    }

    public IList<Order> Orders
    {
        get
        {
            return new new ReadOnlyCollection<Order>(_orders);
        }
    }

    public void Add(Order order)
    {  
        order.User = this;
        _orders.Add(order);
    }

    public void Remove(Order order)
    {
        if (_orders.Contains(order)
        { 
            order.User = null;
            _orders.Remove(order);
        }
    }
}

The second approach clearly has it’s benefits, but it also increase the number of lines in the code (imagine when few of the collections are in the entity). So the question is, is it worth the trouble?

1

Important question: in the first example, where does the code that enforces the business invariant live?

The first approach looks simple and straight forward because all the hard stuff has been moved somewhere else!

So, in order:

public User 
{
    public IList<Order> Orders { get; set; }
}

This is bad, because the constraint on the list of users has been moved outside the entity.

user.getOrders().add(invariantViolatingOrder);

Your second approach is better, but still has problems

public User 
{
    public IList<Order> Orders
    {
        get
        {
            return new new ReadOnlyCollection<Order>(_orders);
        }
    }
}

This protects the collection against change, but it doesn’t protect the Order’s from being changed in a way that violates the invariant.

user.getOrders().getOrder(2).invariantViolatingChange();

The order entity can enforce an invariant of its own state, but it can’t enforce an invariant of the combined state. (Contrived example: this particular user is only allowed $20 dollars of orders. You have to check the entire collection to verify that it’s ok to change this one order).

The easiest way to ensure that an invariant is enforced at every point that changes the state of an entity, is to make sure there is only one place where the state can be changed, and enforce it there. In OO terms, this is “encapsulation”.

public User 
{
    public IList<Order> Orders
    {
        get
        {
            // By returning a copy of the state, we ensure that nothing
            // outside the user can change its state.
            IList<Order> readOnly = new List<Order>();

            for (Order o : _orders) {
                // Turtles all the way down: this depends on the order
                // providing copies of its own state.
                readOnly.add(o.clone());
            }

            return readOnly ;
        }
    }
}

Another possibility is to treat the internal state of an entity as an immutable value type. Getting immutable state is perfectly fine, because there’s no way that the immutable state can be used to violate the business invariant.

But again, this second solution is turtles all the way down; you have to be careful about including entities in a value object.

This is one reason why the IEnumerable interface is used. This allows your public getter property to return a collection you can iterate over but cannot add or remove items from. The underlying read/write collection could be an IList.

public class User
{
    public User()
    {
        orders = new List<Order>();
    }

    private IList<Order> orders;

    public IEnumerable<Order> Orders
    {
        get { return orders; }
    }

    public Order AddOrder(Product p)
    {
        Order o = new Order(this, p);

        orders.Add(o);

        return o;
    }
}

Now you can tightly control how orders get added by forcing clients of your code to use the “AddOrder” method. Furthermore, you can’t accidentally add an order for another User to this User. You can even go so far as to mark the Order constructor internal so code outside this assembly is unable to create orders without a User:

public class Order
{
    internal Order(User user, Product product)
    {
        User = user;
        Product = product;
    }

    public Product Product { get; private set; }
    public User User { get; private set; }
}

And a sample usage:

User user = usersRepository.Find(123);
Product product = productsRepository.Find(321);

// Compiler won't allow this because the Order constructor is marked "internal"
Order order = new Order(user, product);

// This is allowed because the User class is in the same Assembly as the Order class
Order order = user.AddOrder(product);

This approach also guards against trying to add an order to a user where the Order object references a different user:

User user1 = usersRepository.Find(123);
User user2 = usersRepository.Find(1003);
Product product = productsRepository.Find(30);

// Create the "order" object for "user2"
Order order = new Order(user2, product);

// Add the "order" object to "user1" -- oops!
user1.Orders.Add(order);

The code above compiles fine, but results in a runtime error that could quite possibly be difficult to track down.

I realize this expanded beyond your original question, but I think looking at the larger system and the other use cases is essential for providing an answer.

In my opinion, you don’t need a “read only collection” or a clone of the existing collection. If your Order class implements encapsulation properly, no one can change the data for an Order without the business rules being enforced by the Order object itself, so there is no need to clone objects in memory.

  1. Create a private field as an IList or ICollection
  2. Create a public getter property returning the read/write collection as an IEnumerable object
  3. Each object in the collection must implement encapsulation properly so even if some other object changes the data, the data is still changed according to the business rules

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật