private function apply(array $data)
    {
        // Discard any values that do not exist in this object
        $data = array_intersect_key($data, get_object_vars($this));

        // Type coercion and class expectations are not run on null values
        $values = array_filter($data, static function ($value) {
            return null !== $value;
        });

        $types   = array_intersect_key($this->types(), $values);
        $expects = array_intersect_key($this->expects(), $values);

        foreach ($types as $key => $type) {
            settype($data[$key], $type);
        }

        foreach ($expects as $key => $class) {
            if (false === $data[$key] instanceof $class) {
                throw new \DomainException(sprintf(
                    'Expected value of `%s` to be an object of `%s` type',
                    $key,
                    $class
                ));
            }
        }

        foreach ($data as $key => $value) {
            $this->$key = $value;
        }

        $this->validate();
    }