diff --git a/platform-parent/platform-utils/pom.xml b/platform-parent/platform-utils/pom.xml
index 0578697fa..94591fe86 100644
--- a/platform-parent/platform-utils/pom.xml
+++ b/platform-parent/platform-utils/pom.xml
@@ -25,6 +25,16 @@
spring-context
provided
+
+ ch.qos.logback
+ logback-core
+ provided
+
+
+ org.slf4j
+ slf4j-api
+ provided
+
\ No newline at end of file
diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidationRule.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidationRule.java
new file mode 100644
index 000000000..3ad88a3f1
--- /dev/null
+++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidationRule.java
@@ -0,0 +1,43 @@
+package ru.spcex.platform.utils.validation;
+
+import ru.spcex.platform.utils.enumeration.EnumMessage;
+import ru.spcex.platform.utils.enumeration.IEnumId;
+
+import java.util.Optional;
+
+/**
+ * Правило валидации, описывающее какую ошибку возвращать и при каких условиях
+ * Все необходимые данные берет из контекста
+ * @param контекст
+ */
+public interface IValidationRule {
+ Optional validate(C context);
+
+ /**
+ * для логов
+ */
+ default String ruleName() {
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * если у валидатора был вызван метод проверить все, этот метод может использоваться для более тонкой настройки
+ * по дефолту - выполнять проверку даже если предыдущие выполнены с ошибками
+ */
+ default boolean runIfErrorsPresent() {
+ return true;
+ }
+
+ //*************** методы для сокращения кода ***************
+ default Optional of(IEnumId error) {
+ return Optional.of(new EnumMessage(error));
+ }
+
+ default Optional of(IEnumId error, Object... args) {
+ return Optional.of(new EnumMessage(error, args));
+ }
+
+ default Optional empty() {
+ return Optional.empty();
+ }
+}
diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidator.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidator.java
new file mode 100644
index 000000000..a9d688c70
--- /dev/null
+++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidator.java
@@ -0,0 +1,41 @@
+package ru.spcex.platform.utils.validation;
+
+
+import ru.spcex.platform.utils.enumeration.EnumMessage;
+
+import java.util.Collection;
+import java.util.Optional;
+
+/**
+ * Валидатор, в который можно добавлять правила проверок.
+ * Будет выполнять их в том порядке, в котором их добавляли
+ */
+public interface IValidator {
+ /**
+ * метод для добавления правил валидации
+ * если положить правило с некорректными типами, при вызове validate будет ClassCastException
+ */
+ void addRule(IValidationRule> rule);
+
+ /**
+ * если хотим добавить проверки другого валидатора
+ * они будут выполняться только если проверки этого успешно пройдены
+ * возвращает себя
+ */
+ IValidator chain(IValidator anotherValidator);
+
+ /**
+ * валидирует все (подходит для случаев когда проверки не полагаются на то что предыдущие проверки прошли нормально)
+ */
+ Collection validateAll();
+
+ /**
+ * валидирует до первой ошибки
+ */
+ Optional tillFirstError();
+
+ /**
+ * валидирует все, возвращает первую ошибку - для совместимости
+ */
+ Optional validateAllReturnFirst();
+}
diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidatorContext.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidatorContext.java
new file mode 100644
index 000000000..0a4d8d365
--- /dev/null
+++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidatorContext.java
@@ -0,0 +1,3 @@
+package ru.spcex.platform.utils.validation;
+
+public interface IValidatorContext {}
diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/ValidatorImpl.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/ValidatorImpl.java
new file mode 100644
index 000000000..7cc384b55
--- /dev/null
+++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/ValidatorImpl.java
@@ -0,0 +1,106 @@
+package ru.spcex.platform.utils.validation;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import ru.spcex.platform.utils.enumeration.EnumMessage;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Валидатор, предназначенный для правил у которых есть "контекст" валидации
+ */
+public class ValidatorImpl implements IValidator {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private List> rules = new ArrayList<>();
+ private C context;
+ private IValidator chainedValidator;
+
+ public ValidatorImpl(C context) {this.context = context;}
+
+ public ValidatorImpl(C context, IValidationRule>... rules) { //
+ this.context = context;
+ for (IValidationRule> rule : rules) {
+ addRule(rule);
+ }
+ }
+
+ public void setContext(C context) {
+ this.context = context;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void addRule(IValidationRule> rule) {
+ rules.add((IValidationRule) rule);
+ }
+
+ @Override
+ public IValidator chain(IValidator anotherValidator) {
+ if (chainedValidator == null) {
+ chainedValidator = anotherValidator;
+ } else {
+ chainedValidator.chain(anotherValidator);
+ }
+ return this;
+ }
+
+ @Override
+ public Collection validateAll() {
+ List errors = new ArrayList<>();
+ Instant begin = Instant.now();
+ for (IValidationRule rule : rules) {
+ if (rule.runIfErrorsPresent() || errors.size() == 0) {
+ Optional validationError;
+ validationError = rule.validate(context);
+ validationError.ifPresent(errors::add);
+ if (validationError.isPresent()) {
+ log.debug("validating stage: {} error", rule.ruleName());
+ } else {
+ log.debug("validating stage: {} complete", rule.ruleName());
+ }
+ }
+ }
+ log.debug("validation {} in {} ms",
+ errors.size() == 0 ? "successfully completed" : "completed with errors",
+ Instant.now().toEpochMilli() - begin.toEpochMilli());
+ if (chainedValidator != null && errors.size() == 0) errors.addAll(chainedValidator.validateAll());
+ return errors;
+ }
+
+ @Override
+ public Optional tillFirstError() {
+ Instant begin = Instant.now();
+ for (IValidationRule rule : rules) {
+ log.debug("validating stage: {}", rule.ruleName());
+ Optional validationError;
+ validationError = rule.validate(context);
+ if (validationError.isPresent()) {
+ log.debug("validation stage: {} completed with error {} in {} ms",
+ rule.ruleName(), validationError.get().getSubject().getId(),
+ Instant.now().toEpochMilli() - begin.toEpochMilli());
+ return validationError;
+ }
+ }
+ log.debug("validation successfully completed in {} ms", Instant.now().toEpochMilli() - begin.toEpochMilli());
+ if (chainedValidator != null) return chainedValidator.tillFirstError();
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional validateAllReturnFirst() {
+ Collection allErrors = validateAll();
+ if (allErrors.size() > 0) {
+ EnumMessage error = allErrors.iterator().next();
+ return Optional.of(error);
+ } else
+ return Optional.empty();
+ }
+
+ public C getContext() {
+ return context;
+ }
+}