diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
index cecedd4..8188a6b 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
@@ -11,7 +11,9 @@
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
 use Drupal\Core\Entity\EntityDisplayBase;
+use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
 use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\Validator\ConstraintViolationList;
 
 /**
  * Configuration entity that contains widget options for all components of a
@@ -214,16 +216,41 @@ public function extractFormValues(FieldableEntityInterface $entity, array &$form
    * {@inheritdoc}
    */
   public function validateFormValues(FieldableEntityInterface $entity, array &$form, FormStateInterface $form_state) {
+
+    // Collect the violations per field, because there might be constraints
+    // which have to take into account multiple fields.
+    $violations_per_field = [];
     foreach ($entity as $field_name => $items) {
+
+      /** @var \Drupal\Core\Field\FieldItemListInterface $items */
       // Only validate the fields that actually appear in the form, and let the
       // widget assign the violations to the right form elements.
       if ($widget = $this->getRenderer($field_name)) {
         $violations = $items->validate();
         if (count($violations)) {
-          $widget->flagErrors($items, $violations, $form, $form_state);
+          foreach ($violations as $violation) {
+            // @fixme: The old API does not provide the constraint from a given
+            //   violation.
+            if ($violation->getConstraint() instanceof CompositeConstraintBase) {
+              foreach ($violation->coversFields() as $violation_field_name) {
+                $violations_per_field[$violation_field_name][] = $violation;
+              }
+            }
+            else {
+              $violations_per_field[$field_name][] = $violation;
+            }
+          }
         }
       }
     }
+
+    foreach ($violations_per_field as $field_name => $violations) {
+      if ($widget = $this->getRenderer($field_name)) {
+        $violation_list = new ConstraintViolationList($violations);
+        $widget->flagErrors($entity->$field_name, $violation_list, $form, $form_state);
+      }
+    }
+
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php
new file mode 100644
index 0000000..9c78572
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase.
+ */
+
+namespace Drupal\Core\Entity\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Provides a base class for constraints validating multiple fields.
+ *
+ * The constraint must be defined on entity-level; i.e., added to the entity
+ * type. See ... @todo. fix this.
+ *
+ * With that the $values in CompositeConstraintValidatorBase::doValidate() will
+ * return an array keyed with 'name', 'uid' and 'entity', with 'entity' being
+ * the actual full entity.
+ */
+abstract class CompositeConstraintBase extends Constraint {
+
+  /**
+   * An array of entity fields which should be passed to the validator.
+   *
+   * @return string[]
+   *   An array of field names.
+   */
+  abstract public function coversFields();
+
+}
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index 909abc3..adc339b 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -257,7 +257,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setTranslatable(TRUE)
       ->setSetting('max_length', 60)
       ->setDefaultValue('')
-      ->addConstraint('CommentName', array());
+      ->addConstraint('CommentName');
 
     $fields['mail'] = BaseFieldDefinition::create('email')
       ->setLabel(t('Email'))
diff --git a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php
index d387542..15611e1 100644
--- a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php
+++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\comment\Plugin\Validation\Constraint;
 
-use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
 
 /**
  * Supports validating comment author names.
@@ -17,7 +17,7 @@
  *   label = @Translation("Comment author name", context = "Validation")
  * )
  */
-class CommentNameConstraint extends Constraint {
+class CommentNameConstraint extends CompositeConstraintBase {
 
   /**
    * Message shown when an anonymous user comments using a registered name.
@@ -40,4 +40,11 @@ class CommentNameConstraint extends Constraint {
    */
   public $messageMatch = 'The specified author name does not match the comment author.';
 
+  /**
+   * {@inheritdoc}
+   */
+  public function coversFields() {
+    return ['name', 'uid'];
+  }
+
 }
diff --git a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
index f6397e0..8d52479 100644
--- a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
+++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
@@ -8,6 +8,7 @@
 namespace Drupal\comment\Plugin\Validation\Constraint;
 
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintValidatorBase;
 use Drupal\user\UserStorageInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\comment\CommentInterface;
@@ -46,40 +47,35 @@ public static function create(ContainerInterface $container) {
   /**
    * {@inheritdoc}
    */
-  public function validate($items, Constraint $constraint) {
-    /** @var CommentNameConstraint $constraint */
-    if (!isset($items)) {
+  public function validate($value, Constraint $constraint) {
+    if (!isset($value)) {
       return;
     }
-    /** @var CommentInterface $comment */
-    $comment = $items->getEntity();
 
-    if (!isset($comment)) {
-      // Looks like we are validating a field not being part of a comment,
-      // nothing we can do then.
-      return;
-    }
-    $author_name = $items->first()->value;
+    $entity = $value->getEntity();
+
+    $author_name = $entity->name->value;
+    $owner_id = (int) $entity->uid->target_id;
 
     // Do not allow unauthenticated comment authors to use a name that is
     // taken by a registered user.
-    if (isset($author_name) && $author_name !== '' && $comment->getOwnerId() === 0) {
+    if (isset($author_name) && $author_name !== '' && $owner_id === 0) {
       $users = $this->userStorage->loadByProperties(array('name' => $author_name));
       if (!empty($users)) {
         $this->context->addViolation($constraint->messageNameTaken, array('%name' => $author_name));
       }
     }
     // If an author name and owner are given, make sure they match.
-    elseif (isset($author_name) && $author_name !== '' && $comment->getOwnerId()) {
-      $owner = $comment->getOwner();
+    elseif (isset($author_name) && $author_name !== '' && $owner_id) {
+      $owner = $this->userStorage->load($owner_id);
       if ($owner->getUsername() != $author_name) {
         $this->context->addViolation($constraint->messageMatch);
       }
     }
 
     // Anonymous account might be required - depending on field settings.
-    if ($comment->getOwnerId() === 0 && empty($author_name) &&
-      $this->getAnonymousContactDetailsSetting($comment) === COMMENT_ANONYMOUS_MUST_CONTACT) {
+    if ($owner_id === 0 && empty($author_name) &&
+      $this->getAnonymousContactDetailsSetting($entity) === COMMENT_ANONYMOUS_MUST_CONTACT) {
       $this->context->addViolation($constraint->messageRequired);
     }
   }
diff --git a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
index 4a56198..1be9696 100644
--- a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
+++ b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
@@ -37,7 +37,7 @@ protected function setUp() {
    */
   public function testValidation() {
     $entity_type = 'entity_test_constraint_violation';
-    $entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
+    $entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'name' => 'test', 'type' => 'test2'));
     $display = entity_get_form_display($entity_type, $entity_type, 'default');
     $form = array();
     $form_state = new FormState();
@@ -55,6 +55,7 @@ public function testValidation() {
 
     $errors = $form_state->getErrors();
     $this->assertEqual($errors['name'], 'Widget constraint has failed.', 'Constraint violation is generated correctly');
+    $this->assertEqual($errors['type'], 'Multiple fields are validated', 'Constraint violation is generated correctly');
   }
 
 }
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php
index 48b27ab..9d83bed 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php
@@ -44,6 +44,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     ));
     $fields['name']->addConstraint('FieldWidgetConstraint', array());
 
+    $fields['type']->setDisplayOptions('form', array(
+      'type' => 'string',
+      'weight' => 0,
+    ));
+
+    $fields['type']->addConstraint('EntityTestCompositeConstraint', array());
+
     return $fields;
   }
 
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestCompositeConstraint.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestCompositeConstraint.php
new file mode 100644
index 0000000..8e6c4d5
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestCompositeConstraint.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity_test\Plugin\Validation\Constraint\EntityTestCompositeConstraint.
+ */
+
+namespace Drupal\entity_test\Plugin\Validation\Constraint;
+
+use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
+
+/**
+ * Constraint with multiple fields.
+ *
+ * @Plugin(
+ *   id = "EntityTestCompositeConstraint",
+ *   label = @Translation("Constraint with multiple fields.")
+ * )
+ */
+class EntityTestCompositeConstraint extends CompositeConstraintBase {
+
+  public $message = 'Multiple fields are validated';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function coversFields() {
+    return ['name', 'type'];
+  }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestCompositeConstraintValidator.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestCompositeConstraintValidator.php
new file mode 100644
index 0000000..7c3dcee
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestCompositeConstraintValidator.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity_test\Plugin\Validation\Constraint\EntityTestCompositeConstraintValidator.
+ */
+
+namespace Drupal\entity_test\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+class EntityTestCompositeConstraintValidator extends ConstraintValidator {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($value, Constraint $constraint) {
+    if (!isset($value)) {
+      return;
+    }
+
+    $entity = $value->getEntity();
+
+    if ($entity->name->value === 'test' && $entity->type->value === 'test2') {
+      $this->context->addViolation($constraint->message);
+    }
+
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintValidatorBaseTest.php b/core/tests/Drupal/Tests/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintValidatorBaseTest.php
new file mode 100644
index 0000000..53cb9a5
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintValidatorBaseTest.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintValidatorBase.
+ */
+
+namespace Drupal\Tests\Core\Entity\Plugin\Validation\Constraint;
+
+use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
+use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
+use Drupal\Core\Field\FieldItemList;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintValidatorBase
+ * @group Validation
+ */
+class CompositeConstraintValidatorBaseTest extends UnitTestCase {
+
+  /**
+   * Tests validation.
+   */
+  public function testValidate() {
+    $constraint = new TestConstraint();
+    $validator = new TestConstraintValidator();
+    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context->expects($this->never())
+      ->method('addViolation');
+    $validator->initialize($context);
+
+    $items = $this->getMockedFieldItems('example value');
+    $validator->validate($items, $constraint);
+
+    $validator = new TestConstraintValidator();
+    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context->expects($this->once())
+      ->method('addViolation');
+    $validator->initialize($context);
+    $items = $this->getMockedFieldItems('invalid value');
+    $validator->validate($items, $constraint);
+  }
+
+  /**
+   * Get Mocked field items for the given value.
+   *
+   * @param string $value
+   *   The value to set.
+   *
+   * @return \Drupal\Core\Field\FieldItemList
+   */
+  protected function getMockedFieldItems($value) {
+    $entity = $this->getMock('Drupal\Core\Entity\ContentEntityInterface');
+
+    $definition = $this->getMock('\Drupal\Core\TypedData\ComplexDataDefinitionInterface');
+    $definition->expects($this->any())
+      ->method('getPropertyDefinitions')
+      ->willReturn([]);
+    $item = new StringItem($definition);
+    $item->value = $value;
+
+    $items = $this->getMock('\Drupal\Core\Field\FieldItemListInterface');
+    $items->expects($this->any())->method('first')->willReturn($item);
+
+    $entity->expects($this->once())
+      ->method('get')
+      ->with('normal_field')
+      ->willReturn($items);
+
+    $entity_adapter = EntityAdapter::createFromEntity($entity);
+    $definition = $this->getMock('\Drupal\Core\TypedData\DataDefinitionInterface');
+    return new FieldItemList($definition, NULL, $entity_adapter);
+  }
+
+}
+
+namespace Drupal\Tests\Core\Entity\Plugin\Validation\Constraint;
+
+use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
+use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintValidatorBase;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Test composite constraint.
+ */
+class TestConstraint extends CompositeConstraintBase {
+
+  public $message = "invalid";
+
+  /**
+   * An array of entity fields which should be passed to the validator.
+   *
+   * @return string[]
+   *   An array of field names.
+   */
+  public function coversFields() {
+    return ['normal_field'];
+  }
+}
+
+/**
+ * Test composite constraint validator.
+ */
+class TestConstraintValidator extends ConstraintValidator {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($value, Constraint $constraint) {
+    if (!isset($value)) {
+      return;
+    }
+
+    $entity = $value->getEntity();
+    if ($entity->get('normal_field')->first()->value !== 'example value') {
+      $this->context->addViolation($constraint->message);
+    }
+  }
+
+}
