• Типичная задача на создание велосипеда.

  • Проблема code reuse в ORM при совместной разработке.

  • Как аннотации Java позволяют создавать собственные стандарты за чашечкой кофе.

  • Стандарты на разработку подсистем\компонент в ORM.

Типичная задача на создание велосипеда

При работе с ORM (object relation mapping) все время преследует ощущение постоянного создания монолитного приложения — один раз привязался к какой‑либо сущности (например, справочник Контрагенты) и весь код заполнен ссылками на эту конкретную реализацию.

А разве с обычными библиотеками кода не так? Библиотеки Java (package) в отличии от ORM сущностей имеют только зависимости, когда одна библиотека зависит от другой, но это успешно решается через maven. Да ссылка в коде на конкретную библиотеку также создает трудности при переходе на подобную даже в простых случаях ( пример, Java Logging: история кошмара / Хабр (habr.com) ), но это можно решить классами обертками, если предполагается сменить библиотеку на другую реализацию. В ORM все сложнее поскольку оно отражает явные (по foreign key \ constraints ) или неявные по дизайну СУБД связи между таблицами, и такие зависимости maven‑подобным подходом не решишь Maven — Introduction (apache.org).

Можно ли независимо разрабатывать в ORM совместимые между собой справочник «Контрагентов» и использующий его документ «Платежное поручение», но при этом избежать жестких зависимостей? Спасут ли нас микросервисы? Быть или не быть — вот в чем вопрос?

Возьмем практическую задачу на 1С. ORM 1С это хороший пример визуального Low code ORM (object relation mapping) с возможностями слияния метаданных.

О организации ORM 1С хорошо написано тут Как мы в 1С: Предприятии работаем с моделями данных (или «Почему мы не работаем с таблицами?») | 1С:Зазеркалье (1c.ru) .

За мою практику мне часто приходилось автоматизировать на 1С учет нерезидентов, учет которых ведется без плана счетов РФ.

Для этой задачи типовая 1С Бухгалтерия избыточна поскольку:

  • План счетов для управленческого учета у каждого нерезидента свой, но соответствует стандартам IFRS\GAAP.

  • Типовые документы (Платежные поручения, накладные и т.д.) не подходят, поскольку реализованы для РФ.

  • Нужен не только финансовый (бухгалтерский) , но и управленческий учет с тем же планом счетов.

  • Необходимость англоязычного перевода метаданных.

«1С:Бухгалтерия КОРП МСФО» — самая популярная программа для бухгалтерии, ведения учета по МСФО для отдельных компаний! (1c.ru), для этой задачи не очень подходит поскольку подразумевает перекладку учета РФ в МСФО (IFRS). В случае учета нерезидента речь идет о учете по МСФО, без перекладок \ мэппинга.

В идеале хотелось бы собрать подобную систему из

  • 1С Библиотека стандартных подсистем 1С:Библиотека стандартных подсистем (1c.ru) в полном виде (далее 1С БСП).

  • Журнал бухгалтеского учета «хозрасчетный» с поддержкой управленческого и финансового учета + стандартные отчеты (оборотно‑сальдовые ведомости, главные книги, анализ счета и т. д.).

  • Операционный блок уже будет написан свой — поскольку это уже алгоритмы формирования проводок пишутся под нужный план счетов МСФО,а он к сожалению нестандартный.

Почему западные решения (SAP, Sun Account и т. д.) менее эффективны — это тема отдельной статьи, и поверьте соотношение цена\время\результат не в их пользу, и их устриц с ними я уже наелся. Например, вопрос эмуляции двойной записи (для полноценной проводки) — в западных системах это постоянная боль.

Но в 1С тоже не все гладко – тот же 1С БСП как продукт, поставляется разобранным т.е. его нужно еще собрать с вставками кода (обсуждалось тут Конфигурация на БСП с минимальными усилиями - Форум.Инфостарт (infostart.ru) ).

А собранный он только в типовой 1С Бухгалтерии и других конфигурациях,и в итоге приходится брать 1С Бухгалтерию — игнорировать ненужное, дорабатывать нужное. При таком подходе, есть свои очевидные недостатки, которые проявляются при попытках обновлять БСП или делится отдельными подсистемами.

Мечта собирать решение ORM из отдельных компонент все не дает покоя… Ведь неоперационный блок (ОС, НМА, РБП, материалы) тоже как правило имеет единые алгоритмы, но разные печатные формы. Любой более менее крупный проект, требует большей независимости в разработке, а не ожидания пока один сделает справочник «Контрагентов», а другой уже будет его прикручивать к «Платежному поручению»

Проблема code reuse в ORM при совместной разработке.

Возьмем еще более простой пример для любой ORM. Вася хочет сделать самый лучший справочник контрагентов, а Василиса самое лучшее платежное поручение, которое использует справочник контрагентов. Если Вы думаете, что справочник контрагентов это просто — Вы не правы. Минимальный функционал для РФ

  • Сам справочник с реквизитами (ИНН\КПП\ОГРН ) возможностями поиска.

  • Различная связанная контактная информация.

  • Проверка и ведение адресов по ФИАС (классификатор адресов).

  • Банковские реквизиты контрагента.

А для международной версии тоже самое, но с учетом специфики региона (IBAN, SWIFT т.д.). В реальной жизни по контрагентам идет учет договоров, они бывают клиентами и постащиками или госорганами, физлицами, индивидуальными предпринимателями и как следствие функционал растет почти до уровня CRM и в терминах 1С это подсистема! И каждый раз изобретать велосипед не хочется. А еще любой хороший код должен соответствовать best practice, содержать обработку ошибок, быть оптимальным и прочее... То что Вася и Василиса справляться при совместной разработке в 1С это очевидно, но какая связь будет между этими метаданными в терминах 1С?

В типовой конфигурации вот такая

У Васи проблем нет — на его справочник(и) ссылаются, а у Василисы проблема — ее Платежное поручение полностью зависит от реализации Васи (наименования реквизитов\справочников, типы и т. д.). Поскольку в 1С по описанию метаданных может сразу сгенерировать форму (преимущество RAD), то эти ссылки проникнут и туда. В коде тоже будет достаточно отсылок к конкретной реализации справочника Контрагенты. Получаем два следствия

  • Разработка Василисы возможна, когда Вася набросает хотябы структуру метаданных, т. е. о независимой разработке уже речи нет.

  • Если кому‑то больше нравится справочник Контрагентов Коли («КонтрагентыКоли»)_ и платежное поручение Василисы, то оторваться от справочника Контрагентов Васи можно только перекодированием решения.

  • Для распространения своего решения «Платежного поручения» Василисе придется тянуть и конкретную реализацию справочника контрагентов.

Таким образом справочник Контрагентов Васи будет иметь гарантированный круг поклонников\иц, а Платежное поручение Василисы будут использовать только любители покодировать. Не получается у Васи и Василисы синергии. А если с годами (как это часто бывает) Вася перестанет совершенствовать свое решение или игнорировать требования Василисы в пользу других, лучшего справочника Контрагентов и лучшего Платежного поручения не получится.

Как аннотации Java позволяют создавать собственные стандарты за чашечкой кофе.

Кто то скажет, а пусть Вася и Василиса оформят свои решения в виде Микросервисов, используя SOAP, JSON и подобные технологии. Они могут даже не встречаться, а когда встретятся Василисе достаточно сделать перекладку данных с преобразованиями типов в рамках своих API. Но тут опять Василиса будет опять в невыгодном положении, поскольку основную работу по преобразованиям (в строго‑типизированном Java это кошмар) придется сделать ей. А если кто‑то захочет сделать отчет по платежам в разрезе контрагентов нерезидентов (признак у контрагента) — просто и без нарушений концепции микросервиса это сделать нельзя, даже если данные находятся в одном Instance СУБД.

Поэтому — возвращаемся обратно к ORM и сделаем это на Java с аннотациями (Можно на JPA,но как увидите роли это не играет).

Прежде чем что‑то писать Васе и Василисе нужно договорится о стандартах. В данном примере на Java, но наверняка в других языках есть похожие механизмы.

1) Определится какой ORM используется напр JPA или расширенные реализации Spring DATA, Hibernate. Это будет важно, когда придется делать различные отчеты в разрезе платежных поручений и реквизитов контрагентов (соединение). Иначе проблемы будут как у микросервисов (см выше).

2) Договорится о универсальном идентификакторе (ключе) записей в таблицах (далее УИД). В 1С для этого используется GUID который неявно генерируется ORM платформы. Это позволит из платежного поручения легко ссылаться на записи, разных реализаций справочника контрагентов.

3) Способ предоставления данных. В нашем примере будут использоваться процедуры get и set для каждого реквизита объекта. Оба разработчика могут называть их как угодно (даже одинаковые по смыслу) главное чтобы для чтения использовался get*, а для установки значения set*. Это распространенная практика в стандартах Java, например, в JSF для связи метода представляющего значение и места на странице HTML, желающие могут почитать Геттеры и сеттеры в Java | Getters and Setters (javarush.com).

4) Формат аннотации, который будут использоваться для мэппинга get и set.

Итак создаем Package Standards.

Определим класс FieldValue он нужен для того чтобы передавать между методами поле любого типа. В Java жесткая типизация, кроме того разработчики пишут независимо и Вася может сделать ИНН типа Integer, а Василиса типа String

package Standards;
import java.math.*;
import java.util.*;

//Позволяет возвращать значение поля таблицы\объекта независимо от его типа
public class FieldValue {

    private BigInteger IDValue;
    private Long LongValue;
    private String StringValue;
    private Date DateValue;
    private String FieldType;

    //Конструкторы для каждого типа данных, для упрощения идентификации типа фиксируем его
    public FieldValue(BigInteger IDValue) {
        this.IDValue = IDValue;
        this.FieldType = "BigInteger";
    }

    ;
    public FieldValue(String StringValue) {
        this.StringValue = StringValue;
        this.FieldType = "String";
    }

    ;
    public FieldValue(Date DateValue) {
        this.DateValue = DateValue;
        this.FieldType = "Date";
    }

    ;
    public FieldValue(Long LongValue) {
        this.LongValue = LongValue;
        this.FieldType = "Long";
    }

    ;
    public String toString() {
        switch (FieldType) {
            case "BigInteger":
                return this.IDValue.toString();
            case "String":
                return this.StringValue;
            case "Date":
                return this.DateValue.toString();
            case "Long":
                return this.LongValue.toString();
            default:
                return null;
        }
    }
;

}

Определяем нашу аннотацию, это по сути определение интерфейса специального вида с префиксом @ . Там мы определим свойства аннотации которые будем использовать для мэппинга get* и set* методов. Что такое аннотация? Lesson: Annotations (The Java™ Tutorials > Learning the Java Language) (oracle.com)

“Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.

Annotations have a number of uses, among them:

Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.

Compile‑time and deployment‑time processing — Software tools can process annotation information to generate code, XML files, and so forth.

Runtime processing — Some annotations are available to be examined at runtime.”

Стало понятнее? Я думаю нет, потому что важно не что это, а как они обрабатываются. Это будет видно на конкретном примере.

Мы определили аннотацию, которая доступна при исполнении (@Retention(RetentionPolicy.RUNTIME), применяется к любому элементу класса @Target(ElementType.TYPE), может применятся к одному классу несколько раз (@Repeatable(LinkSubsystemS.class))

package Standards;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(LinkSubsystemS.class)
public @interface LinkSubsystem {
    String LinkedClass();
    String LinkedGetter();
    String TargetSetter();
}

Поскольку эта аннотация может повторятся для нескольких мэппингов, нам нужно создать еще один класс LinkSubsystemS

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

public @interface LinkSubsystemS {

    LinkSubsystem[] value();

   }

Аннотация сама по себе работать не будет, если для нее нет процедуры обработки. Напишем собственный парсер аннотации. Именно этот класс дает возможность использовать аннотации. В Java package java.lang.reflect.* позволяет работать с метаданными класса (т. е. из программы смотреть для любого класса его методы, свойства). Вот тут творится та самая магия аннотаций

import java.lang.reflect.*;
import java.math.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LinkSubsystemParser {

    //Исполняем метод линкованого объекта для получения информации. Для Setter нужно передавать устанавливаемые значения это сложнее но тоже возможно стандартизировать
    //Достаточно передавать только определение метаданных класса Class<?> (в данном случае платежное поручение) ,
    //LinkedObject - объект который используем в платежном поручении (конкретный экземпляр справочника контрагентов), универсальный LikedObjectID, и название set* метода чтобы по нему вычислить get* метод
    //Возвращаем универсальный для нас объект FieldValue
    public FieldValue ExecuteGetMethodByMap(Class<?> ClassConsumer, Object LinkedObject, BigInteger LikedObjectID, String SetMethodName) {
        try {
            //Внимание!!! переменные я назвал с *Counterpart* только для понимания, на самом деле метод может мэппить геттеры и сеттеры любых классов
            //Получаем аннотации которые указали у класса потребителя (платежное поручение, которое использует контрагентов). Аннотации получаются из описания метаданных
            LinkSubsystem LinkedCounterpartGetters[] = ClassConsumer.getAnnotation(LinkSubsystemS.class).value();
//Вот тут вычисляем метод get справочника контрагентов,  соотвествующий методу set платежного поручения
//Обходим привязанную аннотацию, она может состоять из нескольких привязок
            for (LinkSubsystem LinkedCounterpartGetterItem : LinkedCounterpartGetters) {
                //Если в соответствие нужный нам метод set то по нему можно получить метод get справочника контрагентов
                //Вот так работать не будет LinkedCounterpartGetterItem.TargetSetter()==SetMethodName потомучто String это объект в отличии от string
                if (LinkedCounterpartGetterItem.TargetSetter().equals(SetMethodName)) {
                    Method CounterpartGetter = LinkedObject.getClass().getMethod(LinkedCounterpartGetterItem.LinkedGetter(), BigInteger.class);
                    Object GetterResult = CounterpartGetter.invoke(LinkedObject, LikedObjectID);
                    return (FieldValue) GetterResult;
                };

            }
            return null;

        } catch (Throwable ex) {
            System.out.println(ex);

            return null;
        }
    }
;
}

Теперь Вася может создавать класс Supplier даже не уведомляя Василису о наименованиях, но следуя оговоренным стандартам. Здесь представлены классы обертки без JPA чтобы сократить демо код

package BestCounterpartDir;
import java.math.BigInteger;
import java.util.HashMap;
//Класс соотвествует записи постащика в СУБД  с несколькими телефонами
public class SupplierData {

    public BigInteger ID;
    public String FullName;
    public Long INN;
    public String Telephone;//Основной телефон
    public HashMap<BigInteger, String> Telephones;

    public SupplierData(BigInteger ID, String FullName, Long INN, String Telephone, HashMap<BigInteger, String> Phones) {
        //Простая инициализация
        this.ID = ID;
        this.FullName = FullName;
        this.INN = INN;
        this.Telephone = Telephone;
        this.Telephones = null;
    }
;

}

//Класс соотвествует справочнику поставщиков в СУБД . Реквизиты с префиксом Selected отражают текущего выбранного поставщика
//Выбор поставщика осуществляется методом selectByName
public class Suppliers {

    private BigInteger IDCounter;

    private BigInteger SelectedID;
    private String SelectedFullName;
    private Long SelectedINN;
    private String SelectedTelephone;

    //Заглушка Здесь должна быть релазиция таблицы поставщиков, можно все сделать через JPA, но кода будет много
    //Для примера делаем просто HashMap
    private HashMap<String, SupplierData> SuppliersTable;

    //Для простоты примера, заполняем начальные данные прямо в конструкторе
    public Suppliers() {
        //Заглушка. Чтобы не загромождать демо код инициализацию  класса данными делаем сразу в конструкторе
        SuppliersTable = new HashMap<String, SupplierData>();
        if (IDCounter == null) {
            IDCounter = BigInteger.valueOf(5000000);
        };
        IDCounter = this.IDCounter.add(BigInteger.valueOf(1));
        SuppliersTable.put("ООО 1С", new SupplierData(IDCounter, "ООО 1С", 7709860400L, "+7 495 688 90 02", null));
        IDCounter = this.IDCounter.add(BigInteger.valueOf(1));
        SuppliersTable.put("ООО SAP", new SupplierData(IDCounter, "ООО SAP", 7705058323L, "8 800 200 01 28", null));
        IDCounter = this.IDCounter.add(BigInteger.valueOf(1));
        SuppliersTable.put("ООО Такском", new SupplierData(IDCounter, "ООО Такском", 7704211201L, " 8 495 730 73 45", null));
    }    ;
    //Поик и выбор поставщика если он еще не выбран
    public BigInteger selectByName(String NameMask) {
        SupplierData SelectedSupplier = SuppliersTable.get(NameMask);
        if (SelectedSupplier == null) {
            return null;
        }
        //Устанавливаем выбранного из справочника поставщика, своего рода кэш
        this.SelectedID = SelectedSupplier.ID;
        this.SelectedFullName = SelectedSupplier.FullName;
        this.SelectedINN = SelectedSupplier.INN;
        this.SelectedTelephone = SelectedSupplier.Telephone;
        return SelectedSupplier.ID;
    }    ;
   
    //Интерфейсный метод геттеры get* значение поля. Если поставщик не выбран он ищется по ID
    //
    public FieldValue getFullName(BigInteger ID) {
        //Если контрагент выбран возвращем значение, в перспективе  можно реализовать автопоиск по ID
        return (this.SelectedID != null & this.SelectedID.equals(ID) ? new FieldValue(this.SelectedFullName) : null);
    }

    ;        
    //Если контрагент выбран возвращем значение, в перспективе  можно реализовать автопоиск по ID

    public FieldValue getPhone(BigInteger ID) {
        return (this.SelectedID != null & this.SelectedID.equals(ID) ? new FieldValue(this.SelectedTelephone) : null);
    }    ;        
    public FieldValue getINN(BigInteger ID) {
        return (this.SelectedID != null & this.SelectedID.equals(ID) ? new FieldValue(this.SelectedINN.longValue()) : null);
    };
}

Василиса может создать свое платежное поручение, используя отладочную версию справочника контрагентов, где только нужные ей get* методы. В методе setCounterPart она использует парсер аннотаций который сделан ранее в Package Standards

package BestPaymentOrder;

import java.math.*;
import java.util.*;
import Standards.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
public class PaymentOrder {
    private Date PaymentDate;
    private Integer PaymentAmount;
    //По соглашению ID типа BigInteger
    private BigInteger CounterPartID;
    private String CounterPartName;
    private String TAXNumber;
    private String Telephone;
    //Конструкторы нужно оставлять по умолчанию - они не наследуются public PaymentOrder(){}; 
    //Устанавливаем дату время , контрагента и его ревизиты используя данные аннотаций. По сути тут идет обработка аннотаций, в которых содержится мэппинг getters, setters
    public void setCounterPart(BigInteger SelectedCounterpartID, Object CounterpartObj) throws Throwable {
        this.CounterPartID = SelectedCounterpartID;

        // Используем наш Parser аннотаций, используем мэппинг в методах аннотаций чтобы исполнить соответствующий get* контрагента для  соответствующего set* платежного поручения
        LinkSubsystemParser AnnotationParser = new LinkSubsystemParser();
        FieldValue GetterResult = AnnotationParser.ExecuteGetMethodByMap(this.getClass(), CounterpartObj, SelectedCounterpartID, "setTelephone");
        this.Telephone = GetterResult.toString();

        GetterResult = AnnotationParser.ExecuteGetMethodByMap(this.getClass(), CounterpartObj, SelectedCounterpartID, "setTAXNumber");
        //Заметьте у контрагента  ИНН это число, а у платежного поручения это строка. Можно делать и более сложные преобразования если за основу взять XML
        this.TAXNumber = GetterResult.toString();

        GetterResult = AnnotationParser.ExecuteGetMethodByMap(this.getClass(), CounterpartObj, SelectedCounterpartID, "setCounterPartName");
        this.CounterPartName = GetterResult.toString();

    }    ;
    
    public void setPayment(Date PaymentDate, Integer PaymentAmount) {
        this.PaymentDate = PaymentDate;
        this.PaymentAmount = PaymentAmount;
    }    ;
    public void PrintPaymentOrder() {
        System.out.println("Дата " + this.PaymentDate);
        System.out.println("Сумма " + this.PaymentAmount);
        System.out.println("Получатель " + this.CounterPartName);
        System.out.println("ИНН Получателя " + this.TAXNumber);
        System.out.println("Телефон " + this.Telephone);
    }    ;
 
    public void setTAXNumber(FieldValue FldTAXNumber) {/*код с преобразованием типов*/
    }    ;
    public void setTelephone(FieldValue FldTelephone) {/*код с преобразованием типов*/
    }    ;
    public void setCounterPartName(FieldValue FldCounterPartName) {/*код с преобразованием типов*/
    };
}

И наконец вишенка на торте — как Василисе подключить справочник контрагентов Василия. Она просто в аннотациях устанавливает мэппинг между get* методами Васи и своими set* методами. Если захочется подключить справочник контрагентов Коли достаточно сделать тоже самое и это будет работать

Если кому то захочется использовать платежное поручение Василисы со справочником Пети, достаточно унаследовать класс class MyPaymentOrder extends PaymentOrder {}; и аннотировать его.

package codereuse;
import BestCounterpartDir.*;
import BestPaymentOrder.*;
import Standards.*;
import java.math.*;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

@LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getFullName", TargetSetter = "setCounterPartName")
@LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getPhone", TargetSetter = "setTelephone")
@LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getINN", TargetSetter = "setTAXNumber")
class MyPaymentOrder extends PaymentOrder {};
public class CodeReuse {
    public static void main(String[] args) {
        // TODO code application logic here

        BigInteger SelectedCounterPartID; 
        Suppliers CounterPart = new Suppliers();
        SelectedCounterPartID = CounterPart.selectByName("ООО 1С");

        MyPaymentOrder PO = new MyPaymentOrder();
        try {
            Calendar SysDate = new GregorianCalendar(2023, 03, 07);
            PO.setPayment(SysDate.getTime(), 55000);
            PO.setCounterPart(SelectedCounterPartID, CounterPart);
            PO.PrintPaymentOrder();

        } catch (Throwable e) {
            return;
        }

    }
}

И результат

Дата Fri Apr 07 00:00:00 MSK 2023

Сумма 55000

Получатель ООО 1С

ИНН Получателя 7709860400

Телефон +7 495 688 90 02

Стандарты  на разработку подсистем\компонент в ORM

С помощью механизма аннотаций можно разрабатывать свои стандарты. Мы можем не только устанавливать  соответствия между методами get* и set* для свойств класса, но и для наборов записей getRows* setRows* . Получится что-то аналогичное DAO библиотекам.

Вопросы которые возникают на этом пути:

Есть ли подобные решения на базе которых может развиться стандарт (не обязательно Java) ?

Меня он очень интересует, и возможно в комментариях подскажут.

Как сделать реализацию стандарта оптимальной по производительности?

Проблемы современных реализаций ORM написаны тут, но они решаемы.

Концепция ORM как двигатель прогресса — выдержит ли ее ваша СУБД? / Хабр (habr.com)

Концепция ORM как двигатель прогресса – выявит слабое место Вашей СУБД / Хабр (habr.com)

Delayed durability поможет вашему ORM увеличить производительность на 50% и более, если Вы только будете использовать … / Хабр (habr.com)

Готовность ORM к горизонтальному маштабированию?

Без полноценной реализации этой темы, невозможно решать вопросы с производительностью.

Язык мой – враг мой. Архитектору о будущем 1С | 1CUnlimited | Дзен (dzen.ru)

Если выражаться языком ООП мы пытаемся достичь слабой связанности между классом PaymentOrder и Supplier см цитату:

“Two or more classes are said to have loose coupling when they do not rely on each other to operate. If one class cannot be created without creating another class first, for example, their coupling is said to be tight.”

Очевидно, что внедрение такого стандарта возможно только при спонсировании заинтересованным производителем софта, ведь рынок компонент \ подсистем не возникает на пустом месте. 1С достигла успехов в партнерских решениях со статусом «1С Совместимо» Справочник «Внедренные решения» (1c.ru) . Но решения там являются либо дополнительными подсистемами для типовых конфигураций 1С. Либо самостоятельные решения, но с обменами для типовых конфигураций 1С т. е. все привязано тем или иным способом к типовым решениям 1С. По сравнению со «свободным» ПО, это конечно плюс в направлении совместимости, но ограничения четко видны.

В теории таже 1С могла бы разработать БСП и платформу 1С Предприятие с полноценной разработкой в ORM относительно независимых подсистем. Но визуально в стиле RAD (Rapid application development) это требует усилий, а не добавление очередной «фичи». Если делать это в стиле Java мы получим огромное количество кода и система 1С перестанет быть RAD. Вы же не хотите этого или все‑таки хотите? Я думаю, что в прикладных Frameworks появление подобных стандартов более реально, чем в стандартах Java. Ведь Java и его конкуренты, ориентированы на другие цели, нежели просто прикладная разработка. Подписывайтесь на следующие серии «1C без ограничений» на нашем канале t.me/Chat1CUnlimited . Код могу выложить на популярный, но полностью доступный в РФ аналог github.

Комментарии (40)


  1. OlegZH
    00.00.0000 00:00

    Любопытно. Хотелось бы, однако, понять, можно ли не вводить явно в код на Java сущности, относящиеся к конкретным реквизитам?

    С точки зрения ООП, ПлатёжноеПоручение есть связный набор объектов, включая, собственно, сам платёж, потом есть Плательщик, Получатель и прочие реквизиты.

    Да! Кстати. Вы не напомните, как появляются платёжные поручения и что они делают (какие регистры затрагивают)?


    1. 1CUnlimited Автор
      00.00.0000 00:00

      Любопытно. Хотелось бы, однако, понять, можно ли не вводить явно в код на Java сущности, относящиеся к конкретным реквизитам?

      Можно - как в ADO ADO — Википедия (wikipedia.org) Там для каждой таблицы или view можно получать row а уже для row есть коллекция fields . В этом случае названия конкретных полей содержатся в field.name т.е. все универсально . Просто если делать связи между полями (напр ИНН) разных объектов, то мэппинг get* и set* всеравно понадобится

      С точки зрения ООП, ПлатёжноеПоручение есть связный набор объектов, включая, собственно, сам платёж, потом есть Плательщик, Получатель и прочие реквизиты

      В представлении в виде класса обертки никто не ограничивает, в том же 1С Документ может содержать несколько табличных частей, как часть "объекта" а в базе это несколько таблиц. Поэтому я предложил Get\Set pattern хотя в ORM JPA автоматически генерируемые сущности в базе представляют отдельные таблицы, а уже сложнее нужно руками писать

      Да! Кстати. Вы не напомните, как появляются платёжные поручения и что они делают (какие регистры затрагивают)?

      Их вводят руками либо на основании другого документа типа счет. Если говорить о типовой 1С Бух 3.0 то документы оформляются, а движения в регистрах делает другой "списание с расчетного счета " подробнее тут Банковские расчетные документы :: Бухгалтерский и налоговый учет в 1С:Бухгалтерии 8 (редакция 3.0). Издание 7 (1c.ru) .

      У него там много регистров, для разных ситуаций (но это реализация типовой конфы 1С)


  1. aleksandy
    00.00.0000 00:00
    +2

    Перефразируя классиков, программист на 1С может писать на 1С на любом языке.

    Ёлки-иголки, можно же было хотя бы стандарты наименования соблюдать? С заглавной буквы в java называют исключительно классы, а не поля и уж тем более локальные переменные.

    Василиса будет опять в невыгодном положении, поскольку основную работу
    по преобразованиям  (в строго-типизированном Java это кошмар) придется
    сделать ей.

    Поэтому мы завяжемся на непонятные строки, которые нигде, кроме времени выполнения не проверяются. И грохнется это всё дело с вероятностью близкой к 146% при первом же использовании данного подхода не автором сего великолепия.

    Настоятельно рекомендую не заниматься хернёй, а перед началом разработки провести хоть какой-нибудь аналитический обзор того, как это принято делать не в 1С.

    В java имеется замечательный инструмент для подобных преобразований, проверенный не одним пользователем, и, что немаловажно, падающим на этапе компиляции, если программист пытается сделать дичь.

    String LinkedClass();

    А почему не java.lang.Class?

    LinkSubsystemS

    По какой причине тут заглавная буква в конце имени?

    Возвращать null из toString()- вообще за гранью.


    1. 1CUnlimited Автор
      00.00.0000 00:00

      Спасибо что напомнили а то сам oracle спустил эти Соглашения (чувствуете разницу со Стандартом) в архив https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html

      Настоятельно рекомендую не заниматься хернёй, а перед началом разработки провести хоть какой-нибудь аналитический обзор того, как это принято делать не в 1С.

      А Вы можете подсказать как принято решать эти проблемы не в 1С без тумана тайного знания.это кстати статья с открытыми вопросами. За mapstruct спасибо я про эти продукты не знал. Но здесь задача разработки совместимых компонент для ORM а это несколько сложнее чем просто мэппинг классов. Просто ищу кто реализовал?


      1. GerrAlt
        00.00.0000 00:00
        +3

        Без тумана тайного знания в мире Java вопросы решаются следующим образом:

        1. Осознаете что же вы на самом деле хотите

        2. Смотрите существующие инструменты максимально покрывающие вашу потребность

        3. Если какой-то один инструмент не нашелся - пытаетесь делить задачу на более простые и снова смотрите что есть подходящего

        4. И вот только когда вы убедились что ничего подходящего совсем нет среди готовых инструментов вы начинаете писать велосипед

        То что вы не искали подходящие инструменты видно хотябы по тому что для мапинга ваших сущностей есть много готовых инструментов, (mupstruct/Dozer/в некотором объеме даже Jackson и т.д.) но вы пошли снизу - увидели в языке вроде бы подходящий инструмент для создания своего инструмента и давай свой велосипед изобретать. Это плохой подход, не надо так - вместо того чтобы изобретать уже изобретенное, покрытое тестами и рабочее, лучше бы сделали что-то действительно новое.

        И да, если пишете на языке - соблюдайте принятый Code Style, иначе ваш код читать будете только вы.


        1. 1CUnlimited Автор
          00.00.0000 00:00

          Так в том то и дело, что просто мэппинга сущностей для независимой разработки компонент на ORM (скажем на JPA ) недостаточно (нужны соглашения о ключах в таблицах и т.д.). Поэтому я и не искал просто мэппинг классов.

          вы пошли снизу - увидели в языке вроде бы подходящий инструмент для создания своего инструмента и давай свой велосипед изобретать.

          Пришлось написать статью-вопрос поскольку как раз такого стандарта хотябы на уровне какого либо framework я не нашел поиском. Возможно в разработке на Java это не так актуально, поскольку тут никто не ждет скорости в разработке, но в прикладном программировании в стиле RAD актуально.

          И да, если пишете на языке - соблюдайте принятый Code Style, иначе ваш код читать будете только вы.

          Да над этим надо поработать, Java для меня не основной язык, но применяю его для микросервисов вокруг 1С . Правда когда увидел что Oracle слил https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html это в архив, удивился. Неужели за 20 лет не появилось более актуальной доки про clean code?


          1. aleksandy
            00.00.0000 00:00

            Так в том то и дело, что просто мэппинга сущностей для независимой разработки компонент на ORM (скажем на JPA ) недостаточно (нужны соглашения о ключах в таблицах и т.д.).

            А по делу, что именно требуется? Что есть "независимая разработка компонент на ORM" в вашем понимании?

            Соглашения о ключах в таблицах, наименования обязательный атрибутов вполне можно реализовать через интерфейсы.


            1. 1CUnlimited Автор
              00.00.0000 00:00

              Изложил в этом комментарии https://habr.com/ru/post/721424/comments/#comment_25311122


          1. aleksandy
            00.00.0000 00:00

            Неужели за 20 лет не появилось более актуальной доки про clean code?

            Во-первых, зачем? Соглашения об наименовании - не так вещь, которую нужно менять каждые полгода.
            Во-вторых, по-моему кто-то путает чистый код и соглашения о наименованиях, форматировании и пр.


  1. alexdoublesmile
    00.00.0000 00:00
    +2

    от такого clean code у любого Java разработчика глаза прослезятся


    1. 1CUnlimited Автор
      00.00.0000 00:00

      Про clean code спасибо но хотелось бы по существу статьи


      1. akhmelev
        00.00.0000 00:00
        +2

        По существу - налицо феноменальная воинствующая некомпетентность. Минусов насыпать могут запросто.

        Почему? Примерно такая аналогия. Представьте: написали криво-коряво-убого тетрис на 1С. Ничтоже сумяшеся заявили на хабре, что сие есть готовый уникальный суперпупер движок для 3D игр для всех платформ, ну и само собой вишенка на торте: приписали DirectX в заголовок. В выводах сообщили, что без проблем можно и в OpenGL и вообще это все неважно - на любом же можно языке тетрис переписать, посоветуйте как лучше.

        На справедливое замечание от игроделов "что это за дичь" говорят "спасибо, но давайте по существу".

        У читателей лукалицо. Это по существу.

        ПисАть как и пИсать нужно только если уж совсем невмоготу. (с) М.Жванецкий.


        1. 1CUnlimited Автор
          00.00.0000 00:00

          По существу - налицо феноменальная воинствующая некомпетентность. Минусов насыпать могут запросто.

          Ну вообщето это статья с открытым вопросом к обсуждению, поэтому я привел конкретный пример. Если Вы имеете на него ответ как в ORM на Java вести независимую разработку совместимых компонент - просто напишите ссылку или цитату. Я как раз и ищу готовые стандарты и решения, и как любителю RAD мне не очень хочется изобретать велосипед.

          Про мэппинги сущностей я ответил выше, для ORM этого недостачно. Если Вы имеете опыт работы с каким либо ORM (необязательно Java) - это интересно.

          Ну а минусы на Хабре всегда могут насыпать, но ради хороших комментариев это можно и потерпеть.


          1. GerrAlt
            00.00.0000 00:00
            +2

            Есть ощущение что у вас проблема на этапе осознания чего вы хотите:

            Вы хотите чтобы поля ваших моделей видели коллеги и могли указывать в своих запросах? Вам достаточно просто вести разработку в одном проекте не в блокноте, любая современная IDE вам все покаже и расскажет про уже созданные в проекте модели.

            Вы хотите сделать в одном проекте РАЗНЫЕ модели навешанные на одни и те же таблицы/колонки БД? Вы хотите странного, тут так не принято ну совсем - почитайте как должен быть устроен маппинг таблиц в рамках существующих ORM.


            1. 1CUnlimited Автор
              00.00.0000 00:00

              Вы хотите сделать в одном проекте РАЗНЫЕ модели навешанные на одни и те же таблицы/колонки БД? Вы хотите странного, 

              Не понимаю откуда такой вывод, причем один и теже таблицы на разные модели. Я вроде пример прозрачный привел. Сформулирую по другому.

              1. Вендор 1СKiller сделал платформу ORM на Java JPA и некоторые базисные вещи (например компоненту\класс "справочник Контрагентов"). Допустим ORM поддерживает несколько СУБД , но классы используем только в одной выбранной СУБД

              2. Василиса сделала на этой ORM компоненту\класс "Платежное поручение" и использовала часть функционала справочника контагентов 1СKiller (банковские счета , инн, наименование)

              3. Вася сделал свой справочник "Контрагенты Васи", который лучше (поддерживает функционал для нерезидентов и вообще) ., но имеет совершенно другую внутреннию структуру таблиц\сущностей

              4. Петя захотел сделать систему платежей из Платежного поручения Василисы, и Контрагентов Васи.

              Как Пете наиболее простым образом соединить эти два решения в ORM 1СKiller, если Василиса использовала для разработки справочник Вендора?

              Чтобы это работало нужны стандарты на уровне структуры классов.именно над JPA , чтобы внутренняя структура таблиц не имела значения кроме вопроса уникального ключа. Просто мэппинг это не решит, возможно где-то подобное реализовывали. В 1С есть все нужное, кроме возможности максимально изолировать эти компоненты.

              При реализации такого ORM возможен рынок\market place совместимых компонент. Иначе только два пути clean кодом склеивать разные решения, ну либо микросервисы которые гораздо более изолированные


              1. aleksandy
                00.00.0000 00:00

                Если Василиса хоть немного имеет представление о проектировании и архитектуре информационных систем, то она

                использовала часть функционала справочника контагентов 1СKiller

                внутри адаптеров своих интерфейсов, предоставляющих данные для её платёжного поручения. Иначе можно сразу ставить крест на переиспользовании её кода с другими источниками без рефакторинга.

                Без вот этого концептуального решения ни один из пункт 4 невозможен. Вернее возможен, но через задн универсальный интерфейс.


                1. 1CUnlimited Автор
                  00.00.0000 00:00

                  внутри адаптеров своих интерфейсов, предоставляющих данные для её платёжного поручения. Иначе можно сразу ставить крест на переиспользовании её кода с другими источниками без рефакторинга.

                  Паттерн Адаптер приходится делать если никаких стандартов\соглашений на интерфейсы нет. Ну получится еще набор clean code примерно как тут Паттерн проектирования «Адаптер» / «Adapter» / Хабр (habr.com) или тут Паттерн адаптер в Java (javarush.com)

                  Я понимаю что много кода для Java среды это само собой разумеющееся, потому что кодом можно закрыть все проблемы.

                  Мне видится что договоренности о стандарте и использование анотации решает вопрос всего парой строк и сильно упрощает жизнь.

                  @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getFullName", TargetSetter = "setCounterPartName")
                  @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getPhone", TargetSetter = "setTelephone")
                  @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getINN", TargetSetter = "setTAXNumber")
                  class MyPaymentOrder extends PaymentOrder {};

                  Например в JSF сумели сделать стандарт для работы со страницами без javascript , но по каким то причинам не стали развивать эту тему . В итоге его удел это простенькие админ интерфейсы


                  1. aleksandy
                    00.00.0000 00:00

                    Паттерн Адаптер приходится делать если никаких стандартов\соглашений на интерфейсы нет.

                    Мужик, ты непробиваемый. Я же писал

                    внутри адаптеров своих интерфейсов, предоставляющих данные для её платёжного поручения.

                    Сделал интерфейс - задал стандарт/соглашение общения со своим сервисом.

                    Тут выше уже писали, что "налицо феноменальная воинствующая некомпетентность", полностью поддерживаю. Объяснять что-то бесполезно, всё равно понимание о чём говорят отсутствует полностью.

                    Попробую показать код.

                    /**
                     * Вендор 1СKiller сделал платформу ORM на Java JPA и 
                     * некоторые базисные вещи (например компоненту\класс 
                     * "справочник Контрагентов").
                     */
                    @Entity
                    class OneCKillerEntity {
                      long id;
                      String name;
                      String account;
                      String individualTaxNumber;
                    }
                    
                    class PaymentOrderVasilisa {
                      /**
                       * Василиса сделала на этой ORM компоненту\класс "Платежное поручение" 
                       * и использовала часть функционала справочника контагентов 1СKiller 
                       * (банковские счета , инн, наименование)
                       */
                      String badImplementaion(OneCKillerEntity data) {
                        return String.format(
                          "%d - %s %s", 
                          data.getId(), 
                          data.getAccount(), 
                          data.getIndividualTaxNumber()
                        );
                      }
                    
                      /**
                       * Внутри адаптеров своих интерфейсов, предоставляющих данные 
                       * для её платёжного поручения.
                       */
                      String goodImplementation(PaymentOrderData data) {
                        return String.format("%s - %s", data.getCode(), data.getDescription());
                      }
                    }
                    
                    /**
                     * Свой интерфейс, предоставляющий данные для платёжки.
                     */
                    interface PaymentOrderData {
                      String getCode();
                      String getDesctiption()
                    }
                    
                    /**
                     * Реализация интерфейса, использующая сущность из 1CKiller.
                     */ 
                    class OneCKillerPaymentOrderData implements PaymentOrderData {
                      final OneCKillerEntity entity;
                    
                      OneCKillerPaymentOrderData(OneCKillerEntity entity) {
                        this.entity = entity;
                      }
                    
                      public String getCode() {
                        return Long.toString(entity.getId());
                      }
                    
                      public String getDescription() {
                        return data.getAccount() + ' ' + data.getIndividualTaxNumber();
                      }
                    }
                    
                    /**
                     * Вася сделал свой справочник "Контрагенты Васи", 
                     * который лучше (поддерживает функционал для нерезидентов и 
                     * вообще) ., но имеет совершенно другую внутреннию структуру 
                     * таблиц\сущностей
                     */
                    class VasiliyEntity {
                      BigInteger primaryKey;
                      Account account;
                      Customer customer;
                    }
                    
                    /**
                     * Этот класс - по факту единственное, что требуется от Пети, 
                     * чтобы в его коде можно было использовать сущности Васи и
                     * сервис для платёжек Василисы.
                     */
                    class PeterPaymentOrderData implements PaymentOrderData {
                      final PeterPaymentOrderData entity;
                    
                      PeterPaymentOrderData(VasiliyEntity entity) {
                        this.entity = entity;
                      }
                    
                      public String getCode() {
                        return entity.getPrimaryKey().toString();
                      }
                    
                      public String getDescription() {
                        return data.getAccount().getNumber() + ' ' + data.getCustomer().getIndividualTaxNumber();
                      }
                    }

                    Причём, когда появится Герман Акакиевич с Акулиной Кузьминичной и вообще наворотят работу через космический астрал, а не БД, то для использования Василисиного сервиса платёжек им всё равно будет достаточно только реализовать интерфейс PaymentOrderData.


                    1. 1CUnlimited Автор
                      00.00.0000 00:00

                      Хотел плюс поставить, но кармы не хватило (поставьте кто нибудь ). Хорошо смотреть паттерны на живых примерах, а то в книгах они слишком абстрактно излагаются и учитывая наличия антипаттернов непонятно, когда чтото Best practice а когда нет.

                      У меня вопрос по последнему участку кода. Вы же по сути сделали мэппинг методов справочника контрагентов на сущности платежного поручения?

                      public String getCode() {
                          return entity.getPrimaryKey().toString();
                        }
                      
                        public String getDescription() {
                          return data.getAccount().getNumber() + ' ' + data.getCustomer().getIndividualTaxNumber();
                        }

                      Я сделал примерно тоже самое но через аннотацию и единую! процедуру обработки (parser) , где можно заложить общую логику обработки ошибок, логгирование и т.д. как плюс.

                      Итоговый код для Пети у меня получается короче (не прописываю обращения к методам контрагентов) . Почему с Вашей точки зрения реализация через аннтотацию хуже? Я понимаю что менее универсально, но мы говорим стандартизации и разработки совместимых компонентов.

                      @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getFullName", TargetSetter = "setCounterPartName")
                      @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getPhone", TargetSetter = "setTelephone")
                      @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getINN", TargetSetter = "setTAXNumber")
                      class MyPaymentOrder extends PaymentOrder {
                      };


                      1. aleksandy
                        00.00.0000 00:00

                        единую! процедуру обработки (parser),

                        Это хорошо работает на мелких синтетических примерах. В реальной жизни такие универсальные единые! процедуры через пару-тройку месяцев превращаются в копролиты и лапшдекод, потому что "вот тут надо просто переложить", "вот тут надо переложить, но чуть-чуть подкорректировать", а "тут вообще надо, взять из источника, сходить на госуслуги, результат вернуть". И чем дольше живёт проект, тем больше появляется таких "вот тут".

                        можно заложить общую логику обработки ошибок, логгирование и т.д. как плюс.

                        Если сильно нужна дополнительная логика обработки для гетеров - используй декоратор.

                        Итоговый код для Пети у меня получается короче (не прописываю обращения к методам контрагентов) .

                        И часто у вас команда длинами меряется? Возьми mapstruct, будут те же аннотации, код сгенерируется, руками писать ничего не придётся.

                        Почему с Вашей точки зрения реализация через аннтотацию хуже?

                        Потому что небезопасно, потому что не KISS. С таким подходом требуется лишний код для тестирования корректности имён, типов результата и аргументов методов. А если тестов на это не делать, то с очередным обновлением, когда в третьесторонней библиотеке метод грохнут (переименуют/перенесут в другой класс/поменяют тип возвращаемого результата), всё навернётся во время исполнения, и хорошо, если это исполнение будет на тестовом стенде, а не на проде.

                        В моём подходе код простой и понятен любому джуну, любое ломающее обратную совместимость изменение в третьесторонней библиотеке заваливает компиляцию.


              1. GerrAlt
                00.00.0000 00:00
                +1

                Вендор 1СKiller сделал платформу ORM на Java JPA и некоторые базисные
                вещи (например компоненту\класс "справочник Контрагентов"). Допустим ORM
                поддерживает несколько СУБД , но классы используем только в одной
                выбранной СУБД

                ORM - это про доступ к данным лежащим в БД через парадигму ООП, правильно? Сделать "справочник Контрагентов" можно было в такой ситуации разве что как демонстрационный пример, как оно работает, сам смысл применения ORM в том что такие вещи делаются тривиально в том проекте где требуются, если с помощью этого ORM создание справочника это скольконибудь нетривиальная задача эту стыдобу лучше бы где-нибудь прикопать и никому не показывать, т.к. есть production-ready решения в которых это делается за час.

                Но ок, допустим речь идет о Keycloak - комплексная система работы с аутентификацией/авторизацией пользователей, и вам очень захотелось взять ее к себе в проект и приделать еще что-то. Не вопрос - вы затягиваете ее к себе в проект через dependency и используете что там вам из нее надо. Там и модели построены, и описаны, и разворачивание на разные БД предусмотрено.

                Допустим вы создали еще какую-то таблицу, поверх нее модель, хотите в этой модели делать связи с какими-то моделями из Keycloak (вы уже изучили документацию и хорошо понимаете что делаете) - вообще не проблема, делаете нужные вам поля у себя в модели с указаниями типов, ORM обеспечивает вам корректное раскладывание данных по моделям которы вы сами не делали.

                Вы захотели еще какой-то проект затащить и взять модели из него - да не вопрос, сверились что БД общее поддерживается, добавили скрипты подготовки БД (если надо) из этого проекта, затянули его к себе через dependency. Готово. Хотите создать модель которая будет иметь часть полей из одного проекта, а часть из другого - наздоровье, продумали план обработки сущностей (как читать/как сохранять/как обновлять) и вперед, ORM позаботиться о том чтобы все распихать по таблицам как вы решили.

                Или я что-то в вашей схеме не понимаю, или вам бы чуть глубже ознакомится с имеющимися решениями, почитать документацию, примеры посмотреть как это все выглядит и работает.


                1. 1CUnlimited Автор
                  00.00.0000 00:00

                  ORM - это про доступ к данным лежащим в БД через парадигму ООП, правильно?

                  Да. И в нашей задаче она нужна чтобы не привязываться к табличной реализации т.е. то что выглядит одним классом Контрагенты на самом деле может под капотом состоять из нескольких таблиц

                  Вы захотели еще какой-то проект затащить и взять модели из него - да не вопрос, сверились что БД общее поддерживается, добавили скрипты подготовки БД (если надо) из этого проекта, затянули его к себе через dependency. 

                  Вот тут самое интересное начинается, если в "моделях" нет стандарта на уникальный ключ записей (guid или чтото подобное) , с типами Вы начнете искать "что БД общее поддерживается". А что может быть ключом для справочника пользовалей - да кто что придумает . Ктото ID с типом Integer , Кто то AccountID типа String, а кторо ФИО в трех полях и будет ждать когда тезка придет. И да кодом Вы решите и в Вашей IDE наверняка будет генератор классов для ORM.

                  Вот скажем я захочу этой модели прикрутить свой справочник UserRole а у меня ключ для идентификации пользователя AccountID String, а не Integer как текущей модели. В 1С эти проблемы решаются наличием guid во всех таблицах и соотвественно индексах. Вы наверное адаптер напишете ?

                  P S Я поэтому и попытался на аннотациях показать какой простой вариант я имею ввиду, чтобы делать миниму кода


                  1. GerrAlt
                    00.00.0000 00:00
                    +1

                    Вот тут самое интересное начинается, если в "моделях" нет стандарта на
                    уникальный ключ записей (guid или чтото подобное) , с типами Вы начнете
                    искать "что БД общее поддерживается". А что может быть ключом для
                    справочника пользовалей - да кто что придумает . Ктото ID с типом
                    Integer , Кто то AccountID типа String, а кторо ФИО в трех полях и будет
                    ждать когда тезка придет. И да кодом Вы решите и в Вашей IDE наверняка
                    будет генератор классов для ORM.

                    Вы должно быть шутите. С чего вдруг меня вообще должно волновать какой там первичный ключ в чьей-то там модели? Я саму модель пишу в поле, а не ее ключ, ORM сам делает все что нужно там с этим ключом делать в той модели, хоть UUID там хоть sequence какой.

                    Истинно глаголю вам, откройте документацию тогоже Hibernate, посмотрите примеры.


                    1. 1CUnlimited Автор
                      00.00.0000 00:00

                      Истинно глаголю вам, откройте документацию тогоже Hibernate, посмотрите примеры.

                      Это именно у Hibernate или у JPA тоже есть такой механизм ?

                      Даже если с ключами вопрос решен , следующий уровень это представление свойств

                      У Cправочника ролей есть getUserRoleByName() но перед этим нужно сделать setCurrenUser(UserID)

                      • Скажем у одной реализации справочника пользователей один разработчик написал одну getRowsByName(Name) и для class Row метод getField() , (примерно в стиле ADO от Microsoft)

                      • А в другой реализации getCurrentUserByName(Name) и дальше можно делать getUserID() , getUserFullName etc.

                      В отсуствии стандартов\соглашений на представление информации о сущностях нам адаптер писать?


                      1. aleksandy
                        00.00.0000 00:00

                        В отсуствии стандартов\соглашений на представление информации о сущностях

                        Кто, блядь, мешает эти стандарты задать?! Для этого и придуманы интерфейсы, чтобы специфицировать формат общения с кодом.

                        нам адаптер писать?

                        Необязательно. В случае с "...class Row метод getField()..." лучше подойдёт фасад.


                      1. GerrAlt
                        00.00.0000 00:00
                        +1

                        Это именно у Hibernate или у JPA тоже есть такой механизм

                        Судя по вопросу вы ни туда ни туда не смотрели, обратитесь к @Id

                        Я указал на Hibernate просто как на референсную реализацию, в его документации достаточно хорошо освещены все JPA возможности и его частные доработки (легко понять что к чему относиться по пакетам)

                        У Cправочника ролей есть getUserRoleByName() но перед этим нужно сделать setCurrenUser(UserID)

                        Вы не шутите? А эту вот информацию вы в сопроводительной записке к вашей реализации напишете?

                        В параллельной ветке увидел у вас такое:

                        class MyPaymentOrder extends PaymentOrder 

                        Вы похоже даже не понимаете насколько гиблое дело наследование в моделях. Нет, если вы живете по ватерфоллу, вы можете себе позволить спроектировать очень красивую, аккуратную, "эффективно немногословную" схему моделей. Но почти любое изменение в моделях будет аукаться у вас по всей выстроенной системе типов. Так не надо делать никогда, если только вы на 100% не уверены что абсолютно точно понимаете почему вы именно так поступаете. Как общий паттерн проектирования это абсолютно негодно.

                        И даже если вы хотите наследование - загляните в документацию Hibernate, там прилично написано про то как это делать если уж вы точно уверены что оно вам надо.


                      1. 1CUnlimited Автор
                        00.00.0000 00:00

                        Судя по вопросу вы ни туда ни туда не смотрели, обратитесь к @Id

                        Понял что имели ввиду, но он не стандартизирует представление ID для нашей задачи это всего лишь инструмент с подстройкой по модель данных СУБД

                        Василиса может у себя заложится на простой ключ для работы с контрагентом . Будет ждать чтото подобного от Васи

                        @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
                        @Basic(optional = false)
                        private Integer id; 
                        

                        А Вася у себя сделает в справочнике контрагентов составной

                            @EmbeddedId
                            protected idPK ComplexPK;
                        

                        И что Василисе тогда сохранять в качестве ID выбранного контрагента?

                        Поэтому 1С для всех своих метаданных использует обязательное поле guid , это дает много плюсов (напр прозрачный обмен данными, составные типы в ORM и прочее что в Java посчитают ересью )

                        А потом мы захотим отчет сделать в платежные поручения по контрагентам нерезидентам. По каком ID мы будем соединять если его не стандартизировать.

                        "

                        У Cправочника ролей есть getUserRoleByName() но перед этим нужно сделать setCurrenUser(UserID)

                        Вы не шутите? А эту вот информацию вы в сопроводительной записке к вашей реализации напишете?

                        " setCurrenUser(UserID) это конечно к справочнику пользователей ошибся

                        Вы похоже даже не понимаете насколько гиблое дело наследование в моделях. Нет, если вы живете по ватерфоллу, вы можете себе позволить спроектировать очень красивую, аккуратную, "эффективно немногословную" схему моделей. Но почти любое изменение в моделях будет аукаться у вас по всей выстроенной системе типов. Так не надо делать никогда, если только вы на 100% не уверены что абсолютно точно понимаете почему вы именно так поступаете. Как общий паттерн проектирования это абсолютно негодно.

                        Мне нужно было применить аннотацию к классу, который скажем так в библиотеке. В примере сделал наследование фиктивное наследование, но можно конечно было бы применять ее к определению свойства в классе который использует платежное поручение и справочник. Правда тогда парсер аннотаций нужно переделать.

                        @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getFullName", TargetSetter = "setCounterPartName")
                        @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getPhone", TargetSetter = "setTelephone")
                        @LinkSubsystem(LinkedClass = "Suppliers", LinkedGetter = "getINN", TargetSetter = "setTAXNumber")
                        public PaymentOrder мyPaymentOrder;

                        Вообще когда Вы рассуждаете о годности паттернов проектирования возникает вопрос: Какую библию по архитектуре Вы читаете?

                        Когда я смотрю литературу по паттернам (и антипаттернам) я понимаю что это рецепты организации кода, часто с абстрактными примерами и из за этого читается тяжело. А хотелось бы видеть Best Practice потому что кроме качества кода есть еще Производительность, Обработка ошибок, Логгирование трассировка, Маштабирование и т.д. вот литература по Best practice как то не попадалась, только отдельные статьи иначе бы не появлялись посты типа

                        Есть ли польза от GoF-паттернов? / Хабр (habr.com)


                      1. GerrAlt
                        00.00.0000 00:00
                        +2

                        Василиса может у себя заложится на простой ключ для работы с контрагентом

                        Т.е. Василиса пилит у себя функционал по еще не существующему связанному справочнику, договоренностей никаких, она просто решила что почему бы тому вот будущему справочнику не быть таким? Это проблема организации рабочего процесса и стандартизация в части ключей ничего не даст - у связанного справочника может оказаться такая логика функционирования что несовпадающий ключ это будут мелочи, и чтож теперь, делать стандарт на функционирование всех на свете справочников?

                        Если же мы говорим что справочник таки уже существует - мы просто берем его к себе в проект и смотрим какой там ключ.

                        Ситуацию можно образно описать так: вы много работали с автомобильными заводами, вы пришли на завод заводов и пытаетесь доказать что вот этот вот станок должен обязательно выпускать колеса, иначе без этого куча времени уходит на всякую фигню, хотя и так же понятно что колесо тут подойдет. Но люди которые много лет проектировали этот завод заводов не просто так сделали именно так, и вместо того чтобы доказывать необходимость стандартизации лучше бы почитать насчет того что и как вообще завод заводов делает, возможно настройка его на выпуск колес настолько тривиальная задача что об этом как о задаче даже говорить не принято.

                        Когда я смотрю литературу по паттернам (и антипаттернам) я понимаю что
                        это рецепты организации кода, часто с абстрактными примерами

                        Большинство конкретных примеров вы найдете либо в рабочих проектах коллег, либо в статьях/выступлениях ребят кто любит рассказывать что у них на проектах случалось и как они это решали. Из авторов по JPA/Hibernate могу рекомендовать Vlad Mihalcea и Thorben Janssen


                      1. 1CUnlimited Автор
                        00.00.0000 00:00

                        Т.е. Василиса пилит у себя функционал по еще не существующему связанному справочнику, договоренностей никаких, она просто решила что почему бы тому вот будущему справочнику не быть таким? Это проблема организации рабочего процесса и стандартизация в части ключей ничего не даст - у связанного справочника может оказаться такая логика функционирования что несовпадающий ключ это будут мелочи, и чтож теперь, делать стандарт на функционирование всех на свете справочников?

                        В 1С к этому подошли очень близко , вот картинка

                        Справочник контрагентов с собственным интерфейсом (у него и форма элемента и списка все в одном "классе" описано

                        Связь с платежным поручением идет только по Ссылке (Единый формат ключа для всех объектов) и по реквизитам (свойства) .

                        Я больше скажу - составным типом 1С я могу два справочника одновременно привязать в один реквизит и все благодаря единому формату ключа.

                        Необходимость ссылаться на реквизиты единственное, что приводит к более жесткой связи этих "классов" . Если это развязать красивым образом , то Code reuse улучшится на порядок. Чтото вроде компонентного подхода для ORM. Адаптеры с точки зрения RAD (Rapid application development) дадут слишком много кода.

                        Если про авто говорить - сейчас авто делают на так называемых платформах наберите MQB платформа. На ней получаются совершенно разные авто

                        Просто кроме Java которая застряла в бэкэнде, из за нежелания Oracle вкладываться в красивый Frontend (ну мы не будем показывать на JSF и JavaFX) есть системы где это удобно реализовали, и иногда полезно менять точку обзора


                      1. aleksandy
                        00.00.0000 00:00

                        А хотелось бы видеть Best Practice

                        ЛПП. Ничего такого вам не хочется. Т.к. в комментариях уже не раз объяснили, в том числе я, как надо и как не надо делать. Но вы упёрлись рогом в своё одноэсное видение и не хотите ничего слышать, что противоречит этой точке зрения.

                        Какую библию по архитектуре Вы читаете?

                        Хороший, кстати, вопрос. К вам. Потому как, если судить по статье и диалогу в комментариях, - никакую.

                        Василиса может у себя заложится на простой ключ для работы с контрагентом

                        А Вася у себя сделает в справочнике контрагентов составной

                        Ну, ёжики пушистые, да не должна Василиса, если у неё хоть капелька мозгов имеется, работать в своих сервисах, которые потенциально могут быть переиспользованы где, кем и как угодно, работать с сущностями. Сущность - это сугубо внутренняя часть реализации. Она не должна торчать наружу.


                      1. 1CUnlimited Автор
                        00.00.0000 00:00
                        -1

                        ЛПП. Ничего такого вам не хочется. Т.к. в комментариях уже не раз объяснили, в том числе я, как надо и как не надо делать. Но вы упёрлись рогом в своё одноэсное видение и не хотите ничего слышать, что противоречит этой точке зрения.

                        Я вот думаю откуда появляются статьи типа "100 ошибок которых легко избежать" , "Как не нужно делать " . Это видимо из детства когда ребенку говорили нельзя, но при этом не говорили хотябы две альтернативы.

                        Я Вашу позицию понял и за код с адаптерами спасибо. Но когда речь идет о архитектурных вопросах мне интересно знать откуда автор черпает свои взгляды. Поверьте когда успешно гоняешь 1С на 5 терабайтах и видишь полных стек, начинаешь отличать абстрактные концепции от best practice


                      1. aleksandy
                        00.00.0000 00:00

                        Но когда речь идет о архитектурных вопросах мне интересно знать откуда автор черпает свои взгляды.

                        Личный опыт и соотнесение его абстрактными примерами из книжек, статей, докладов на конференциях и прочим.

                        Поверьте когда успешно гоняешь 1С на 5 терабайтах

                        Так гоняйте дальше, дело-то хозяйское. Что ж в java-разработку полезли с вопросами, ответы на которые упорно не хотите принимать?

                        З.Ы. У меня складывается впечатление, что вы тупо не решаетесь попросить, чтобы кто-то за вас задизайнил систему "как в 1С, но на java". Так я вам вот что скажу, так не получится, слишком разные языки и подходы к разработке. Чтобы пересесть на с 1С на java придётся поломать некоторые привычки.


                      1. 1CUnlimited Автор
                        00.00.0000 00:00

                        У меня складывается впечатление, что вы тупо не решаетесь попросить, чтобы кто-то за вас задизайнил систему "как в 1С, но на java". Так я вам вот что скажу, так не получится, слишком разные языки и подходы к разработке. Чтобы пересесть на с 1С на java придётся поломать некоторые привычки.

                        Зачем как в 1С, когда есть 1С :) . Можно ли сделать лучше чем в 1С при этом чтобы это выглядело как RAD , была возможность независимой разработки за счет стандартизации связей между (классами, компонентами , метаданными как больше нравится) .?

                        Поскольку Java вбирает в себя модные технологии, и сам хорошо стандартизирован была надежда что на нем можно выразить эту идею красиво. Адаптеры в RAD превращают его в noRAD с моей точки зрение.

                        Чтобы пересесть на с 1С на java придётся поломать некоторые привычки.

                        Да привычки в Jave согласен совершенно другие, таже архитектура JavaEE как они ее сами называют

                        1.3 Distributed Multitiered Applications - Java Platform, Enterprise Edition: The Java EE Tutorial (Release 7) (oracle.com)

                        никогда нет ощущения что ты пишешь приложение, а только всегда некую часть.


                      1. aleksandy
                        00.00.0000 00:00

                        это выглядело как RAD

                        Т.е. нужна мышекликательная платформа для мифических пользователей-непрограммистов, которые сами смогут без программистов всё настроить и работать? Уж одинэсник-то должен понимать, что взлетит это чуть позже чем никогда.

                        Всё, я сливаюсь. Обсуждать одно и то же по 100500 раз надоело.


                      1. aleksandy
                        00.00.0000 00:00

                        архитектура JavaEE

                        Это который за ненадобностью отдали в сообщество ещё в 2018? И с тех пор оно (сообщество) пытается хоть как-то сделать так, чтобы его (JakartaEE) использование не причиняло попоболь?

                        Вот только большинство новых проектов выбирает spring/quarkus/micronaut/ktor/etc., а не EE.

                        Хороший пример, ага.


                      1. aleksandy
                        00.00.0000 00:00

                        "100 ошибок которых легко избежать" , "Как не нужно делать " . Это, видимо, из детства когда ребенку говорили нельзя, но при этом не говорили хотя бы две альтернативы.

                        Не, зачастую, такие статьи пишут либо:

                        а) внезапно прозревшие джуны;

                        б) опытные товарищи для непрозревших джунов.


                  1. aleksandy
                    00.00.0000 00:00

                    И в нашей задаче она нужна чтобы не привязываться к табличной реализации т.е. то что выглядит одним классом Контрагенты на самом деле может под капотом состоять из нескольких таблиц

                    Я, кажется, понял. Т.е. класс Контрагент для postgres, mysql, oracle, dbf - это 4 разные реализации, так? Тогда я могу с уверенностью сказать, что дизайн системы - говно. Так быть не должно и работать на длинной дистанции не будет. И уж тем более, ни о каком убийстве 1С можно не мечтать.

                    Ты путаешь понятия домена и сущности. По факту, при описанном подходе класс Контрагент и должен быть адаптером к соответствующей сущности.

                    если в "моделях" нет стандарта на уникальный ключ записей (guid или чтото подобное)

                    То я создам этот стандарт через объявление интерфейсов и базовых классов. И всем пользователям придётся ему следовать.

                    с типами Вы начнете искать "что БД общее поддерживается"

                    Неа, я буду использовать то, что мне нужно. Если какая-то БД что-то не поддерживает, то либо эта БД не будет поддерживаться моим приложением вообще, либо я расширю диалект ORM-а, чтобы научить работать БД с конкретным типом.

                    Вы наверное адаптер напишете ?

                    Можно и так, почему нет? Ещё можно просто в мапинге задать полю пользовательский тип, в котором руками описать все необходимые преобразования. А можно забить и просто через промежуточную таблицу соединить, например.


                    1. 1CUnlimited Автор
                      00.00.0000 00:00

                      Я, кажется, понял. Т.е. класс Контрагент для postgres, mysql, oracle, dbf - это 4 разные реализации, так? Тогда я могу с уверенностью сказать, что дизайн системы - говно. Так быть не должно и работать на длинной дистанции не будет. И уж тем более, ни о каком убийстве 1С можно не мечтать.

                      Зачем так, в том же 1С структура таблиц для разных субд одинакова вплоть до индексов. Просто в ORM никто не запрещает представить Контранетов и связанный с ним справочник Телефонов как одина класс Контрагенты . Я только это имел ввиду.

                      Скажем в типовой бухгалтерии этот справочник выглядит так

                      А внутри базы вот так, но это никого не волнует поскольку все работают через ORM


                      1. aleksandy
                        00.00.0000 00:00

                        Понятнее не стало. Что-то похожее на EAV? Тогда лучше сразу забыть про JPA.


                      1. 1CUnlimited Автор
                        00.00.0000 00:00

                        Обычная реляционная модель, таблица контактной информации имеет поле ссылки на справочник контрагентов. А "класс"\объект метаданных в терминах 1С с точки зрения Java это класс обертка для этих двух сущностей и он ими управляет. На Java такое несложно реализовать и без JPA, конечно в базе JPA управляет Одна сущность Одна таблица, но путем Mapping a Single Entity to Multiple Tables in JPA | Baeldung можно достичь желаемого и в JPA.