diff --git a/modules/order/commerce_order.module b/modules/order/commerce_order.module
index 7686686..3a8e92b 100644
--- a/modules/order/commerce_order.module
+++ b/modules/order/commerce_order.module
@@ -29,6 +29,18 @@ function commerce_order_theme($existing, $type, $theme, $path) {
 }
 
 /**
+ * Implements hook_entity_type_alter().
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
+ */
+function commerce_order_entity_type_alter(array &$entity_types) {
+  // Unset EntityChanged constraint for Commerce order entities.
+  $order_constraints = $entity_types['commerce_order']->getConstraints();
+  unset($order_constraints['EntityChanged']);
+  $entity_types['commerce_order']->setConstraints($order_constraints);
+}
+
+/**
  * Implements hook_ENTITY_TYPE_presave().
  */
 function commerce_order_commerce_order_presave(OrderInterface $order) {
diff --git a/modules/order/src/Entity/Order.php b/modules/order/src/Entity/Order.php
index fbffdce..57f694f 100644
--- a/modules/order/src/Entity/Order.php
+++ b/modules/order/src/Entity/Order.php
@@ -45,7 +45,7 @@ use Drupal\profile\Entity\ProfileInterface;
  *     "id" = "order_id",
  *     "label" = "order_number",
  *     "uuid" = "uuid",
- *     "bundle" = "type"
+ *     "bundle" = "type",
  *   },
  *   links = {
  *     "canonical" = "/admin/commerce/orders/{commerce_order}",
@@ -55,7 +55,10 @@ use Drupal\profile\Entity\ProfileInterface;
  *     "collection" = "/admin/commerce/orders"
  *   },
  *   bundle_entity_type = "commerce_order_type",
- *   field_ui_base_route = "entity.commerce_order_type.edit_form"
+ *   field_ui_base_route = "entity.commerce_order_type.edit_form",
+ *   constraints = {
+ *     "OrderVersion" = {}
+ *   }
  * )
  */
 class Order extends ContentEntityBase implements OrderInterface {
@@ -82,6 +85,16 @@ class Order extends ContentEntityBase implements OrderInterface {
         $this->setEmail($this->getOwner()->getEmail());
       }
     }
+    else {
+      // Increment order version on each save.
+      $saved_entity = $storage->loadUnchanged($this->id());
+      // A change to the order version must add an exception.
+      if ($saved_entity && $saved_entity->getVersion() > $this->getVersion()) {
+        throw new OrderVersionMismatchException('The order has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
+      }
+      $version = $this->getVersion() + 1;
+      $this->setVersion($version);
+    }
 
     // Recalculate the total.
     // @todo Rework this once pricing is finished.
@@ -131,6 +144,21 @@ class Order extends ContentEntityBase implements OrderInterface {
   /**
    * {@inheritdoc}
    */
+  public function getVersion() {
+    return $this->get('version')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setVersion($version) {
+    $this->set('version', $version);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getOrderNumber() {
     return $this->get('order_number')->value;
   }
@@ -389,6 +417,13 @@ class Order extends ContentEntityBase implements OrderInterface {
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields = self::entityKeysBaseFieldDefinitions($entity_type);
 
+    $fields['version'] = BaseFieldDefinition::create('integer')
+      ->setLabel(t('Version'))
+      ->setDescription(t('The order version number, it gets incremented on each save.'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', TRUE)
+      ->setDefaultValue(1);
+
     $fields['order_number'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Order number'))
       ->setDescription(t('The order number displayed to the customer.'))
diff --git a/modules/order/src/Entity/OrderInterface.php b/modules/order/src/Entity/OrderInterface.php
index bdbb3c2..ae8f092 100644
--- a/modules/order/src/Entity/OrderInterface.php
+++ b/modules/order/src/Entity/OrderInterface.php
@@ -37,6 +37,24 @@ interface OrderInterface extends EntityStoreInterface, EntityChangedInterface, E
   public function setOrderNumber($order_number);
 
   /**
+   * Gets the order version identifier.
+   *
+   * @return int
+   *   Version identifier of the order.
+   */
+  public function getVersion();
+
+  /**
+   * Sets the order version identifier.
+   *
+   * @param int
+   *   Version identifier of the order.
+   *
+   * @return $this
+   */
+  public function setVersion($version);
+
+  /**
    * Gets the order state.
    *
    * @return \Drupal\state_machine\Plugin\Field\FieldType\StateItemInterface
diff --git a/modules/order/src/Exception/ExceptionInterface.php b/modules/order/src/Exception/ExceptionInterface.php
new file mode 100644
index 0000000..d46f318
--- /dev/null
+++ b/modules/order/src/Exception/ExceptionInterface.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\commerce_order\Exception\ExceptionInterface.
+ */
+
+namespace Drupal\commerce_order\Exception;
+
+/**
+ * Exception interface for all exceptions thrown by the Order module.
+ */
+interface ExceptionInterface { }
diff --git a/modules/order/src/Exception/OrderVersionMismatchException.php b/modules/order/src/Exception/OrderVersionMismatchException.php
new file mode 100644
index 0000000..30b4496
--- /dev/null
+++ b/modules/order/src/Exception/OrderVersionMismatchException.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\commerce_order\Exception\OrderVersionMismatchException.
+ */
+
+namespace Drupal\commerce_order\Exception;
+
+/**
+ * Thrown when attempting to save an order with wrong version.
+ */
+class OrderVersionMismatchException extends \RuntimeException implements ExceptionInterface { }
diff --git a/modules/order/src/Form/OrderForm.php b/modules/order/src/Form/OrderForm.php
index 5075401..44f4ab1 100644
--- a/modules/order/src/Form/OrderForm.php
+++ b/modules/order/src/Form/OrderForm.php
@@ -66,6 +66,11 @@ class OrderForm extends ContentEntityForm {
       '#type' => 'hidden',
       '#default_value' => $order->getChangedTime(),
     ];
+    // Version must be sent to the client, for later overwrite error checking.
+    $form['version'] = [
+      '#type' => 'hidden',
+      '#default_value' => $order->getVersion(),
+    ];
 
     $last_saved = $this->dateFormatter->format($order->getChangedTime(), 'short');
     $form['advanced'] = [
@@ -158,7 +163,8 @@ class OrderForm extends ContentEntityForm {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
-    $this->entity->save();
+    $order = $this->getEntity();
+    $order->save();
     drupal_set_message($this->t('The order %label has been successfully saved.', ['%label' => $this->entity->label()]));
     $form_state->setRedirect('entity.commerce_order.collection');
   }
diff --git a/modules/order/src/Plugin/Validation/Constraint/OrderVersionConstraint.php b/modules/order/src/Plugin/Validation/Constraint/OrderVersionConstraint.php
new file mode 100644
index 0000000..1f2c4e8
--- /dev/null
+++ b/modules/order/src/Plugin/Validation/Constraint/OrderVersionConstraint.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\commerce_order\Plugin\Validation\Constraint\OrderVersionConstraint.
+ */
+
+namespace Drupal\commerce_order\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Validation constraint for the order version.
+ *
+ * @Constraint(
+ *   id = "OrderVersion",
+ *   label = @Translation("Order version", context = "Validation"),
+ *   type = "entity:commerce_order"
+ * )
+ */
+class OrderVersionConstraint extends Constraint {
+
+  public $message = 'The order has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.';
+}
diff --git a/modules/order/src/Plugin/Validation/Constraint/OrderVersionConstraintValidator.php b/modules/order/src/Plugin/Validation/Constraint/OrderVersionConstraintValidator.php
new file mode 100644
index 0000000..ab3d968
--- /dev/null
+++ b/modules/order/src/Plugin/Validation/Constraint/OrderVersionConstraintValidator.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\commerce_order\Plugin\Validation\Constraint\OrderVersionConstraintValidator.
+ */
+
+namespace Drupal\commerce_order\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Validates the OrderVersion constraint.
+ */
+class OrderVersionConstraintValidator extends ConstraintValidator {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($entity, Constraint $constraint) {
+    if (isset($entity)) {
+      /** @var \Drupal\Core\Entity\EntityInterface $entity */
+      if (!$entity->isNew()) {
+        $saved_entity = \Drupal::entityManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
+        // A change to the order version must add a violation.
+        if ($saved_entity && $saved_entity->getVersion() > $entity->getVersion()) {
+          $this->context->addViolation($constraint->message);
+        }
+      }
+    }
+  }
+
+}
diff --git a/modules/order/src/Tests/OrderConstraintsTest.php b/modules/order/src/Tests/OrderConstraintsTest.php
new file mode 100644
index 0000000..1b0fb68
--- /dev/null
+++ b/modules/order/src/Tests/OrderConstraintsTest.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\commerce_order\Tests\OrderConstraintsTest.
+ */
+
+namespace Drupal\commerce_order\Tests;
+
+use Drupal\commerce_order\Tests\OrderTestBase;
+
+/**
+ * Tests order level validation constraints.
+ *
+ * @group commerce
+ */
+class OrderConstraintsTest extends OrderTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Tests defining order constraints via order type annotations and hooks.
+   */
+  public function testOrderConstraintDefinition() {
+    // Test reading the annotation. There should be a single constraint,
+    // the OrderRevision defined in the order module.
+    // The core EntityChanged constraint should be unset (not available).
+    $order_type = \Drupal::service('entity_type.manager')->getDefinition('commerce_order');
+    $default_constraints = ['OrderVersion' => []];
+    $this->assertEqual($default_constraints, $order_type->getConstraints());
+  }
+
+  /**
+   * Tests order constraints are validated.
+   */
+  public function testOrderConstraintValidation() {
+    $line_item = $this->createEntity('commerce_line_item', [
+      'type' => 'product_variation',
+    ]);
+    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
+    $order = $this->createEntity('commerce_order', [
+      'type' => 'default',
+      'mail' => $this->loggedInUser->getEmail(),
+      'line_items' => [$line_item],
+      'store_id' => $this->store->id(),
+    ]);
+
+    // Validate the order created.
+    $violations = $order->validate();
+    $this->assertEqual(count($violations), 0, 'No violations when validating a default order.');
+    if (count($violations) > 0 && !empty($violations[0])) {
+      $this->pass($violations[0]->getMessage() . $violations[0]->getPropertyPath());
+    }
+
+    // Save the order for version increment.
+    $order->save();
+
+    // Set the version to 1 (first version).
+    $order->set('version', 1);
+    $violations = $order->validate();
+    $this->assertEqual(count($violations), 1, 'Violation found when version is less the last version.');
+    $this->assertEqual($violations[0]->getPropertyPath(), '');
+    $this->assertEqual($violations[0]->getMessage(), 'The order has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
+  }
+
+}
