diff --git a/src/main/jastadd/Constraints.jrag b/src/main/jastadd/Constraints.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..f713c77bc4faad5090def9447b155f98b5e12452
--- /dev/null
+++ b/src/main/jastadd/Constraints.jrag
@@ -0,0 +1,91 @@
+aspect Constraints {
+
+  coll Set<ConstraintViolation> PetriNet.constraintViolations() [new java.util.HashSet()] root PetriNet;
+
+}
+
+aspect ConstraintViolations {
+
+  abstract class ConstraintViolation {
+
+
+    public String toString() {
+      return "Constraint " + getTitle() + " violated in object " + id() + ": " + getMessage();
+    }
+
+    protected ASTNode location;
+
+    public String id() {
+      if (location instanceof PnObject) return ((PnObject) location).getId();
+      if (location instanceof PetriNet) return ((PetriNet) location).getId();
+      return location.toString();
+    }
+
+    protected String title;
+    protected String description;
+    protected String message;
+
+    public String getTitle() { return title; }
+    public String getDescription() { return description; }
+    public String getMessage() { return message; }
+
+  }
+
+  class CheckIdsConstraint extends ConstraintViolation {
+    public CheckIdsConstraint(ASTNode location) {
+      this.location = location;
+      this.title = "CheckIds";
+      this.description = "Checks id attribute existence";
+      this.message = "A node of type '" + location.getClass().getSimpleName() + "' must have an id attribute.";
+    }
+  }
+
+  class CheckIdUniquenessConstraint extends ConstraintViolation {
+    public CheckIdUniquenessConstraint(ASTNode location) {
+      this.location = location;
+      this.title = "CheckIdUniquess";
+      this.description = "Checks id attribute uniqueness";
+      this.message = "The id attribute value " + id() + " of the node of type '" + location.getClass().getSimpleName() + "' is not unique.";
+    }
+  }
+
+  PetriNet contributes new CheckIdsConstraint(this)
+      when checkIdsConstraintViolated()
+      to PetriNet.constraintViolations();
+  PnObject contributes new CheckIdsConstraint(this)
+      when checkIdsConstraintViolated()
+      to PetriNet.constraintViolations();
+}
+
+aspect CoreModelConstraints {
+
+  syn boolean PetriNet.checkIdsConstraintViolated() = getId() == null || getId().equals("");
+  syn boolean PnObject.checkIdsConstraintViolated() = getId() == null || getId().equals("");
+
+  coll List<String> PetriNet.ids() [new ArrayList()] root PetriNet;
+  PnObject contributes getId() to PetriNet.ids();
+
+  syn Map<String, Long> PetriNet.idCount() =
+      ids().stream().collect(Collectors.groupingBy(java.util.function.Function.identity(), Collectors.counting()));
+
+  syn boolean PetriNet.uniqueId(String id) = idCount().get(id) <= 1;
+  syn boolean PnObject.uniqueId(String id) = petriNet().idCount().get(id) <= 1;
+
+  syn Optional<CheckIdUniquenessConstraint> PetriNet.checkIdUniqueness() {
+    if(uniqueId(getId())) return Optional.empty();
+    else return Optional.of(new CheckIdUniquenessConstraint(this));
+  }
+  syn Optional<CheckIdUniquenessConstraint> PnObject.checkIdUniqueness() {
+    if(petriNet().uniqueId(getId())) return Optional.empty();
+    else return Optional.of(new CheckIdUniquenessConstraint(this));
+  }
+
+  PetriNet contributes checkIdUniqueness().get()
+      when checkIdUniqueness().isPresent()
+      to PetriNet.constraintViolations();
+  PnObject contributes checkIdUniqueness().get()
+      when checkIdUniqueness().isPresent()
+      to PetriNet.constraintViolations();
+
+
+}