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..8d9ec95 100644
--- a/modules/order/src/Entity/Order.php
+++ b/modules/order/src/Entity/Order.php
@@ -45,7 +45,8 @@ use Drupal\profile\Entity\ProfileInterface;
  *     "id" = "order_id",
  *     "label" = "order_number",
  *     "uuid" = "uuid",
- *     "bundle" = "type"
+ *     "bundle" = "type",
+ *     "version" = "version",
  *   },
  *   links = {
  *     "canonical" = "/admin/commerce/orders/{commerce_order}",
@@ -55,7 +56,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 {
@@ -81,6 +85,18 @@ class Order extends ContentEntityBase implements OrderInterface {
       if (!$this->getEmail()) {
         $this->setEmail($this->getOwner()->getEmail());
       }
+
+      $this->setVersion(1);
+    }
+    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.
@@ -131,6 +147,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 +420,12 @@ 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);
+
     $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/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);
+        }
+      }
+    }
+  }
+
+}
