diff --git a/core/modules/node/lib/Drupal/node/Field/TitleField.php b/core/modules/node/lib/Drupal/node/Field/TitleField.php
new file mode 100644
index 0000000..b9ad3b3
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Field/TitleField.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Plugin\DataType\TitleField.
+ */
+
+namespace Drupal\node\Field;
+
+use Drupal\Core\Entity\Field\Field;
+
+/**
+ * Defines the node title field with a custom constraint.
+ */
+class TitleField extends Field {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstraints() {
+    $constraints = parent::getConstraints();
+
+    // If the node type is configured to have a title field then it should be
+    // required.
+    $node = $this->getParent();
+    $node_type = node_type_load($node->bundle());
+    if ($node_type && $node_type->has_title) {
+      $constraints[] = \Drupal::typedData()->getValidationConstraintManager()->create('NotNull', array());
+    }
+    return $constraints;
+  }
+}
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index c3ccc3c..083636a 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -155,6 +155,10 @@ public function baseFieldDefinitions() {
       'label' => t('Title'),
       'description' => t('The title of this node, always treated as non-markup plain text.'),
       'type' => 'string_field',
+      'list_class' => 'Drupal\node\Field\TitleField',
+      'property_constraints' => array(
+        'value' => array('Length' => array('max' => 255)),
+      ),
     );
     $properties['uid'] = array(
       'label' => t('User ID'),
@@ -176,6 +180,9 @@ public function baseFieldDefinitions() {
       'label' => t('Changed'),
       'description' => t('The time that the node was last edited.'),
       'type' => 'integer_field',
+      'property_constraints' => array(
+        'value' => array('NodeChanged' => array()),
+      ),
     );
     $properties['comment'] = array(
       'label' => t('Comment'),
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Validation/Constraint/NodeChangedConstraint.php b/core/modules/node/lib/Drupal/node/Plugin/Validation/Constraint/NodeChangedConstraint.php
new file mode 100644
index 0000000..1ca461f
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Plugin/Validation/Constraint/NodeChangedConstraint.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Plugin\Validation\Constraint\NodeChangedConstraint.
+ */
+
+namespace Drupal\node\Plugin\Validation\Constraint;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Validation constraint for the node changed timestamp.
+ *
+ * @Plugin(
+ *   id = "NodeChanged",
+ *   label = @Translation("Node changed", context = "Validation")
+ * )
+ */
+class NodeChangedConstraint extends Constraint {
+
+  public $message = 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.';
+}
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Validation/Constraint/NodeChangedConstraintValidator.php b/core/modules/node/lib/Drupal/node/Plugin/Validation/Constraint/NodeChangedConstraintValidator.php
new file mode 100644
index 0000000..0479e38
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Plugin/Validation/Constraint/NodeChangedConstraintValidator.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Plugin\Validation\Constraint\NodeChangedConstraintValidator.
+ */
+
+namespace Drupal\node\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Validates the NodeChanged constraint.
+ */
+class NodeChangedConstraintValidator extends ConstraintValidator {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($value, Constraint $constraint) {
+    if (isset($value)) {
+      // We are on the field item level, so we need to go two levels up for the
+      // node object.
+      $node = $this->context->getMetadata()->getTypedData()->getParent()->getParent();
+      $id = $node->id();
+      $language = $node->language();
+      if ($id && (node_last_changed($id, $language->id) > $value)) {
+        $this->context->addViolation($constraint->message);
+      }
+    }
+  }
+}
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
new file mode 100644
index 0000000..dcba3e2
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Tests\NodeValidationTest.
+ */
+
+namespace Drupal\node\Tests;
+
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests node validation constraints.
+ */
+class NodeValidationTest extends DrupalUnitTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'entity', 'field', 'text', 'field_sql_storage');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Node Validation',
+      'description' => 'Tests the node validation constraints.',
+      'group' => 'Node',
+    );
+  }
+
+  /**
+   * Set the default field storage backend for fields created during tests.
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->installSchema('node', 'node');
+    $this->installSchema('node', 'node_field_data');
+    $this->installSchema('node', 'node_field_revision');
+
+    // Create a node type for testing.
+    $type = entity_create('node_type', array('type' => 'page', 'name' => 'page'));
+    $type->save();
+  }
+
+  /**
+   * Tests the node validation constraints.
+   */
+  public function testValidation() {
+    $node = entity_create('node', array('type' => 'page', 'title' => 'test'));
+    $violations = $node->validate();
+    $this->assertEqual(count($violations), 0, 'No violations when validating a default node.');
+
+    $node->set('title', $this->randomString(256));
+    $violations = $node->validate();
+    $this->assertEqual(count($violations), 1, 'Violation found when title is too long.');
+    $this->assertEqual($violations[0]->getPropertyPath(), 'title.0.value');
+    $this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => 255)));
+
+    $node->set('title', NULL);
+    $violations = $node->validate();
+    $this->assertEqual(count($violations), 1, 'Violation found when title is not set.');
+    $this->assertEqual($violations[0]->getPropertyPath(), 'title');
+    $this->assertEqual($violations[0]->getMessage(), t('This value should not be null.'));
+
+    // Make the title valid again.
+    $node->set('title', $this->randomString());
+    // Save the node so that it gets an ID and a changed date.
+    $node->save();
+    // Set the changed date to something in the far past.
+    $node->set('changed', 433918800);
+    $violations = $node->validate();
+    $this->assertEqual(count($violations), 1, 'Violation found when changed date is before the last changed date.');
+    $this->assertEqual($violations[0]->getPropertyPath(), 'changed.0.value');
+    $this->assertEqual($violations[0]->getMessage(), t('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'));
+  }
+}
