TDD 和 DDD 入门

发布于 2024-08-05 17:00:32 字数 2154 浏览 2 评论 0原文

我刚刚读完 Eric Evans 写的“领域驱动设计:解决软件核心的复杂性”,我正在尝试编写我的第一个以领域为中心的应用程序(用 C# 编写)。

我们的服务台将使用该应用程序来跟踪向用户分配的计算机。

我绘制了一个简单的类图来反映部分领域。它看起来像这样...

显示两个类的类图:所有者和计算机。计算机和所有者之间有一个名为“分配到”的单向关联 http://www.freeimagehosting.net/uploads/183dd57031.jpg

我还确定了我的第一个功能(将计算机分配给用户)并为其编写了一个测试...

[Test]
public void AllocateToUser()
{
    var user = new Owner("user1");
    var computer = new Computer("computer1");

    computer.Allocate(user);

    Assert.AreEqual(user.Username, computer.AllocatedTo.Username);
}

最后,我编写了代码以使测试通过。

public class Computer
{
    public Computer(string name)
    {
        Name = name;
    }

    public string Name
    { get; private set; }

    public Owner AllocatedTo
    { get; private set; }

    public void Allocate(Owner owner)
    {
        AllocatedTo = owner;
    }
}

public class Owner
{
    public Owner(string username)
    {
        Username = username;
    }

    public string Username
    { get; private set; }
}

到目前为止,一切都很好(我认为)。

然而,显然这些都没有解决持久性问题。我想我需要为计算机引入一个存储库类。也许是这样的:

public class ComputerRepository
{
    public void Store(Computer computer)
    {
        //Some persistence logic here (possibly using NHibernate?)
    }
}

现在我陷入困境。如何确保对计算机分配的用户所做的更改传递到存储库?

我似乎有以下选择:

  1. 修改 Computer 类的 Allocate 方法的实现以实例化 ComputerRepositry 的实例并调用 Store 方法。

  2. 创建接口IComputerRepository;修改 Computer 的构造函数以要求提供实现 IComputerRepository 的类的实例。在 Allocate 方法中,针对这个注入的实例调用 Store。

  3. 创建一个服务 (AllocationService),它将结束对 Allocate 和 Store 的调用。

  4. 将责任传递给客户端,强制调用代码执行两个步骤:

    • 在 Computer 类的实例上调用 Allocate
    • 实例化 ComputerRepository 的实例并调用 Store。

这些似乎都不令人满意:

  1. 很难测试,因为我会直接在 Computer 类中实例化存储库。

  2. 通过使用依赖注入来避免这个问题。然而它仍然很难看,因为每次我想实例化 Computer 时都需要传入 IComputerRepository 的某个实例。

  3. 过于程序化,未能将行为封装在域实体类中。

    过于
  4. 只是看起来很丑。

我应该做什么?

I've just finished reading "Domain-driven design: tackling complexity in the heart of software" by Eric Evans and I'm attempting to write my first domain-centric application (in C#).

The application will be used by our helpdesk to track the allocation of computers to users.

I've sketched a simple class diagram to reflect part of the domain. It looks like this...

Class diagram showing two classes: Owner and Computer. There is a one-way associate between Computer and Owner named 'Allocate to' http://www.freeimagehosting.net/uploads/183dd57031.jpg

I've also identified my first feature (to allocate a computer to a user) and have written a test for it...

[Test]
public void AllocateToUser()
{
    var user = new Owner("user1");
    var computer = new Computer("computer1");

    computer.Allocate(user);

    Assert.AreEqual(user.Username, computer.AllocatedTo.Username);
}

Lastly, I've written code to make the test pass.

public class Computer
{
    public Computer(string name)
    {
        Name = name;
    }

    public string Name
    { get; private set; }

    public Owner AllocatedTo
    { get; private set; }

    public void Allocate(Owner owner)
    {
        AllocatedTo = owner;
    }
}

public class Owner
{
    public Owner(string username)
    {
        Username = username;
    }

    public string Username
    { get; private set; }
}

So far, so good (I think).

However, clearly none of this addresses persistence. I think I need to introduce a repository class for Computer. Something like this perhaps:

public class ComputerRepository
{
    public void Store(Computer computer)
    {
        //Some persistence logic here (possibly using NHibernate?)
    }
}

Now I'm stuck. How do ensure that changes made to a computer's allocated user are passed to the repository?

I seem to have the following options:

  1. Modify the implementation of the Allocate method of the Computer class to instanstiate an instance of ComputerRepositry and invoke the Store method.

  2. Create an interface IComputerRepository; modify the constructor of Computer to demand that an instance of a class implemeting IComputerRepository is supplied. Within the Allocate method, call Store against this injected instance.

  3. Create a service (AllocationService) that will wrap up a call to Allocate and Store.

  4. Pass resposibility to the client, forcing two steps for the calling code:

    • Invoke Allocate on an instance of the Computer class
    • Instantiate an instance of the ComputerRepository and call Store.

None of these seem satisfactory:

  1. is hard to test, as I'd be instantiating the repository directly within the Computer class.

  2. avoids this problem, by using dependency-injection. However it is still ugly, as I need to pass in some instance of IComputerRepository every time I want to instantiate Computer.

  3. is too procedural, failing to encapsulate the behaviour within domain entity classes.

  4. just seems ugly.

What should I be doing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

百思不得你姐 2024-08-12 17:00:32

通常我会将行为和持久性视为两个不同的问题并分别进行测试。

域对象应该不知道存储库的存在(尽管显然不是相反)。

在这种情况下,我们所做的是创建一个控制器(或服务),负责从存储库中加载适当的对象,调用对象上的行为,然后调用存储库来保存更新。

然后,您可以使用模拟存储库测试控制器,以检查控制器是否正在使用更新的对象调用存储库。

Usually I would treat behaviour and persistence as two different concerns and test them separately.

The Domain objects should remain oblivious to the existence of Repositories (though clearly not the other way round).

What we've done in this situation is to create a Controller (or a Service) that is responsible for loading the appropriate objects from their Repositories, invoking the behaviour on the objects then calling the Repositories to persist the updates.

You can then test the controller using a Mock repository to check that the controller is calling the repository with the updated objects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文