DDD - 实体创建业务规则验证

时间:2014-04-28 10:12:10

标签: php oop

我有一个商业规则,例如:

  

如果求职者想要申请职位空缺,请确保申请中使用的简历已经完成且求职者没有   已适用于该职位空缺。如果条件满足,那么   该应用程序将以JobApplicatiion编写。

这就是我提出的:

JobSeeker.php

class JobSeeker {
    private $applications;
    private $resume;

    /** Other irrelevant props **/

    public function apply(Vacancy $vacancy, Resume $resume) {
        // Business rule #1
        if(!$resume->isCompleted()) throw new \Exception('Resume '.$resume->getTitle().' is incomplete.');

        // Business rule #2
        $alreadyApplied = array_filter($this->applications->toArray(), function(JobApplication $application) use($vacancy) {
            return $application->getVacancy() === $vacancy;
        });

        if($alreadyApplied) throw new \Exception('Vacancy '.$vacancy->getTitle().' is already applied');

        // If both rules passed, then create a JobApplication
        $application = new JobApplication($this, $vacancy, $resume);

        $this->applications->add($application);

        return $application;
    }
}

JobApplication.php

class JobApplication {
    private $applicant;
    private $vacancy;
    private $resume;

    public function __construct(JobSeeker $applicant, Vacancy $vacancy, Resume $resume) {
        $this->applicant = $applicant;
        $this->vacancy = $vacancy;
        $this->resume = $resume;
    }
}

如果我希望每个人都只使用

$jobApplication = $jobSeeker->apply($vacancy, $jobSeeker->getResume());

然后没问题。

当有人这样做时会出现问题

$jobApplication = new JobApplication($jobSeeker, $vacancy, $resume);

第二个示例将绕过业务规则验证。

我确实将规则检查分为不同的方法:

JobSeeker.php

class JobSeeker {
    public function canApply() {
        // Here goes those 2 business rules mentioned
    }

    public function apply(Vacancy $vacancy, Resume $resume) {
        if($this->canApply($vacancy, $resume)) {
            return new JobApplication($this, $vacancy, $resume);
        }
    }
}

JobApplication.php

class JobApplication {
    public function __construct(JobSeeker $jobSeeker, Vacancy $vacancy, Resume $resume) {
        if($jobSeeker->canApply($vacancy, $resume)) {
            // Same as before
        }
    }
}

虽然第二种方法保证了业务规则约束,但它非常冗余,仍然无法提供预期的结果。

$jobApplication = new JobApplication($jobSeeker, $vacancy, $resume);

我需要深入了解这一点。

谢谢!

2 个答案:

答案 0 :(得分:2)

根据您的操作方式,我看到它有2个聚合根 JobSeeker Vacancy

Resume类似于用户的个人资料

DDD喜欢使用服务,几乎所有东西。

所以我们有JobSeekerApplicaitonService这项服务将用于外部世界。

JobSeekerApplicaitonService我会添加方法apply

public function apply(JobSeeker $jobSeeker, Vacancy $vacancy);

首先,我们检查是否符合业务规则。 即

$jobSeeker->getResume()->isCompleted();

如果未完成,此检查会抛出错误。

接下来,我们在JobSeekerApplicaitonService创建另一个函数来检查JobSeeker是否已经应用,也可以用于视图,让用户已经看到他已经申请过。

public function hasApplied(JobSeeker $jobSeeker, Vacancy $vacancy);

但是这个方法现在可以在我们的应用函数中使用

$ this-> hasApplied($ jobSeeker,$ vacancy);

在已经应用时再次抛出异常。

您现在可以节省new JobApplication的费用。虽然我会说JobSeekerApplicaitonService存储库并在那里创建它,但它保存在数据库中,因为这就是应用程序服务,委托者。

代码

class JobSeekerApplicaitonService {
    public function apply(JobSeeker $jobSeeker, Vacancy $vacancy) {
        if ($jobSeeker->getResume()->isCompleted()) {
            // throw exception
        } elseif ($this->hasApplied($jobSeeker, $vacancy)) {
            // throw exception
        }
        // save logic or something else you want
    }

    public function hasApplied(JobSeeker $jobSeeker, Vacancy $vacancy) {
        // your check, I would now use the JobApplicationRepository
        return false;
    }
}

答案 1 :(得分:0)

JobSeeker& JobApplication是正确的。 JobSeeker.apply方法充当JobApplications的工厂:

job_application = job_seeker.apply(vacancy, resume)

看起来不错。

然而,以下陈述并没有多大意义:

$jobApplication = new JobApplication($jobSeeker, $vacancy, $resume);

考虑到现实世界,你有没有看到JobApplication在你的桌面上随意地随意爆发?我没有:)在大多数情况下,一个实体是从另一个实体创建的:

employee = company.hire(person)
invoice = employee.create_invoice(customer, invoice_terms)
etc...

如果您发现某个实体正在进行新版修改。在命令处理程序中,它应该引起一阵眉毛。