diff --git a/src/mesh/IZoneDescriptor.hpp b/src/mesh/IZoneDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1fed7c8df7c96539cdf08693e8fb0afc35411912
--- /dev/null
+++ b/src/mesh/IZoneDescriptor.hpp
@@ -0,0 +1,44 @@
+#ifndef I_ZONE_DESCRIPTOR_HPP
+#define I_ZONE_DESCRIPTOR_HPP
+
+#include <mesh/RefId.hpp>
+
+#include <iostream>
+
+class IZoneDescriptor
+{
+ public:
+  enum class Type
+  {
+    named,
+    numbered
+  };
+
+ protected:
+  virtual std::ostream& _write(std::ostream& os) const = 0;
+
+ public:
+  friend std::ostream&
+  operator<<(std::ostream& os, const IZoneDescriptor& zone_descriptor)
+  {
+    return zone_descriptor._write(os);
+  }
+
+  [[nodiscard]] virtual bool operator==(const RefId& ref_id) const = 0;
+
+  [[nodiscard]] friend bool
+  operator==(const RefId& ref_id, const IZoneDescriptor& zone_descriptor)
+  {
+    return zone_descriptor == ref_id;
+  }
+
+  [[nodiscard]] virtual Type type() const = 0;
+
+  IZoneDescriptor(const IZoneDescriptor&) = delete;
+  IZoneDescriptor(IZoneDescriptor&&)      = delete;
+  IZoneDescriptor()                       = default;
+
+  virtual ~IZoneDescriptor() = default;
+};
+
+#endif   // I_ZONE_DESCRIPTOR_HPP
diff --git a/src/mesh/NamedZoneDescriptor.hpp b/src/mesh/NamedZoneDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fbe101421f7f5048019f945622ad4263e8d3e39
--- /dev/null
+++ b/src/mesh/NamedZoneDescriptor.hpp
@@ -0,0 +1,43 @@
+#ifndef NAMED_ZONE_DESCRIPTOR_HPP
+#define NAMED_ZONE_DESCRIPTOR_HPP
+
+#include <mesh/IZoneDescriptor.hpp>
+
+#include <iostream>
+#include <string>
+
+class NamedZoneDescriptor final : public IZoneDescriptor
+{
+ private:
+  std::string m_name;
+
+  std::ostream&
+  _write(std::ostream& os) const final
+  {
+    os << '"' << m_name << '"';
+    return os;
+  }
+
+ public:
+  bool
+  operator==(const RefId& ref_id) const final
+  {
+    return m_name == ref_id.tagName();
+  }
+
+  Type
+  type() const final
+  {
+    return Type::named;
+  }
+
+  NamedZoneDescriptor(const NamedZoneDescriptor&) = delete;
+  NamedZoneDescriptor(NamedZoneDescriptor&&)      = delete;
+  NamedZoneDescriptor(const std::string& name) : m_name(name)
+  {
+    ;
+  }
+  virtual ~NamedZoneDescriptor() = default;
+};
+
+#endif   // NAMED_ZONE_DESCRIPTOR_HPP
diff --git a/src/mesh/NumberedZoneDescriptor.hpp b/src/mesh/NumberedZoneDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e72521d2c69346c4fc46edc82dd0bbedd99e20a
--- /dev/null
+++ b/src/mesh/NumberedZoneDescriptor.hpp
@@ -0,0 +1,39 @@
+#ifndef NUMBERED_ZONE_DESCRIPTOR_HPP
+#define NUMBERED_ZONE_DESCRIPTOR_HPP
+
+#include <mesh/IZoneDescriptor.hpp>
+
+#include <iostream>
+
+class NumberedZoneDescriptor final : public IZoneDescriptor
+{
+ private:
+  unsigned int m_number;
+
+  std::ostream&
+  _write(std::ostream& os) const final
+  {
+    os << '"' << m_number << '"';
+    return os;
+  }
+
+  bool
+  operator==(const RefId& ref_id) const final
+  {
+    return m_number == ref_id.tagNumber();
+  }
+
+ public:
+  Type
+  type() const final
+  {
+    return Type::numbered;
+  }
+
+  NumberedZoneDescriptor(const NumberedZoneDescriptor&) = delete;
+  NumberedZoneDescriptor(NumberedZoneDescriptor&&)      = delete;
+  NumberedZoneDescriptor(unsigned int number) : m_number(number) {}
+  virtual ~NumberedZoneDescriptor() = default;
+};
+
+#endif   // NUMBERED_ZONE_DESCRIPTOR_HPP