paint-brush
इवेंट-ड्रिवेन आर्किटेक्चर: इवेंट डॉक्यूमेंटेशन से स्वचालित डीटीओ जनरेशनद्वारा@dstepanov
2,779 रीडिंग
2,779 रीडिंग

इवेंट-ड्रिवेन आर्किटेक्चर: इवेंट डॉक्यूमेंटेशन से स्वचालित डीटीओ जनरेशन

द्वारा Stepanov Dmitrii20m2022/09/22
Read on Terminal Reader
Read this story w/o Javascript

बहुत लंबा; पढ़ने के लिए

AsyncAPI एक ओपन-सोर्स पहल है जो इवेंट-ड्रिवेन आर्किटेक्चर (EDA) की वर्तमान स्थिति में सुधार करना चाहता है। इस लेख में, मैं आपको बताना चाहूंगा कि मैंने निम्नलिखित कार्य को कैसे हल किया, अर्थात् स्प्रिंगवॉल्फ द्वारा उत्पन्न JSON प्रलेखन का उपयोग करके DTO की पीढ़ी।
featured image - इवेंट-ड्रिवेन आर्किटेक्चर: इवेंट डॉक्यूमेंटेशन से स्वचालित डीटीओ जनरेशन
Stepanov Dmitrii HackerNoon profile picture


सॉफ्टवेयर विकास प्रक्रिया में एक बहुत महत्वपूर्ण चीज जिसे अक्सर एक परियोजना के शुरुआती चरणों में अनदेखा कर दिया जाता है, वह है एपीआई दस्तावेज। इस समस्या के समाधानों में से एक दस्तावेज़ीकरण की स्वचालित पीढ़ी के लिए रूपरेखा है।


प्रोजेक्ट को माइक्रोसर्विसेज में विभाजित करने और इवेंट-ड्रिवेन आर्किटेक्चर का उपयोग करने के मामले में, मैसेज ब्रोकर के माध्यम से प्रसारित घटनाओं का उपयोग करके सेवाओं के बीच बातचीत का निर्माण किया जाता है।


घटना-संचालित वास्तुकला के मामले में दस्तावेज़ीकरण उत्पन्न करने के लिए, AsyncApi है। AsyncAPI एक ओपन-सोर्स पहल है जो इवेंट-ड्रिवेन आर्किटेक्चर (EDA) की वर्तमान स्थिति में सुधार करना चाहता है। AsyncApi में कई जावा उपकरण हैं जो आपको कोड से प्रलेखन उत्पन्न करने की अनुमति देते हैं। इस लेख में, मैंने वर्णन किया है कि इनमें से किसी एक स्प्रिंगवॉल्फ टूल को कैसे सेट किया जाए।


इस लेख में, मैं आपको बताना चाहूंगा कि मैंने निम्नलिखित कार्य को कैसे हल किया, अर्थात् स्प्रिंगवॉल्फ द्वारा उत्पन्न JSON प्रलेखन का उपयोग करके DTO की पीढ़ी।

संकट

स्प्रिंग वुल्फ द्वारा उत्पन्न प्रलेखन संरचना इस तरह दिखती है:


 { "service": { "serviceVersion": "2.0.0", "info": { //block with service info }, "servers": { "kafka": { //describe of kafka connection } }, "channels": { "kafka-channel": { "subscribe": { //... "message": { "oneOf": [ { "name": "pckg.test.TestEvent", "title": "TestEvent", "payload": { "$ref": "#/components/schemas/TestEvent" } } ] } }, //... } }, "components": { "schemas": { "TestEvent": { //jsonschema of component } } } } }


चूंकि jsonschema का उपयोग प्रलेखन में घटकों का वर्णन करने के लिए किया जाता है, इसलिए मैंने इस समस्या को हल करने के लिए jsonschema2pojo लाइब्रेरी का उपयोग करने का निर्णय लिया। हालाँकि, अपनी योजना को लागू करने की कोशिश में, मुझे कई समस्याओं का सामना करना पड़ा:


  • घटकों का वर्णन करने वाली वस्तुओं को निकालने के लिए आपको JSON दस्तावेज़ को अतिरिक्त रूप से पार्स करने की आवश्यकता है। चूंकि jsonschema2pojo jsonschema ऑब्जेक्ट्स को इनपुट के रूप में लेता है, वे घटक ब्लॉक में हैं।
  • jsonschema2pojo बहुरूपता के साथ अच्छी तरह से काम नहीं करता है और AsyncAPI में मौजूद oneOf ब्लॉक से मानक संदर्भों को संभालता नहीं है। इनहेरिटेंस के विवरण के लिए स्कीमा (extends.javaType) में विशेष फ़ील्ड की आवश्यकता होती है, जिसे केवल AsyncAPI दस्तावेज़ में नहीं जोड़ा जा सकता है।
  • चूंकि हमारे मामले में जेनरेट की गई कक्षाओं का उपयोग ब्रोकर से संदेशों को डीसेरियलाइज करने के लिए किया जाना चाहिए, इसलिए वर्णनकर्ताओं और उपप्रकारों का वर्णन करने वाले जैक्सन एनोटेशन को जोड़ना आवश्यक है।


इन सभी समस्याओं ने मुझे jsonschema2pojo पर अपने रैपर को लागू करने की आवश्यकता के लिए प्रेरित किया, जो दस्तावेज़ीकरण से आवश्यक जानकारी निकालेगा, बहुरूपता का समर्थन करेगा, और जैक्सन एनोटेशन जोड़ देगा। परिणाम एक ग्रैडल प्लगइन है जिसके साथ आप स्प्रिंगवॉल्फ एपीआई का उपयोग करके अपनी परियोजना के लिए डीटीओ कक्षाएं उत्पन्न कर सकते हैं। इसके बाद, मैं यह प्रदर्शित करने का प्रयास करूंगा कि प्रलेखन के लिए कक्षाओं को कैसे एनोटेट किया जाए और स्प्रिंगवॉल्फडोक 2 डीटीओ प्लगइन का उपयोग कैसे किया जाए।

दस्तावेज़ीकरण सेटअप

यहां मैं एनम और मैप जैसे गैर-आदिम प्रकारों के लिए कब पीढ़ी की बारीकियों पर विचार करना चाहूंगा। और बहुरूपता के लिए आवश्यक क्रियाओं का भी वर्णन करें।


आइए निम्नलिखित संदेश को देखें:


 @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class TestEvent implements Serializable { private String id; private LocalDateTime occuredOn; private TestEvent.ValueType valueType; private Map<String, Boolean> flags; private String value; public enum ValueType { STRING("STRING"), BOOLEAN("BOOLEAN"), INTEGER("INTEGER"), DOUBLE("DOUBLE"); private final String value; public ValueType(String value) { this.value = value; } } }


इस तरह के संदेश के लिए jsonschema इस तरह दिखेगा:


 { "service": { //... "components": { "schemas": { "TestEvent": { "type": "object", "properties": { "id": { "type": "string", "exampleSetFlag": false }, "occuredOn": { "type": "string", "format": "date-time", "exampleSetFlag": false }, "valueType": { "type": "string", "exampleSetFlag": false, "enum": [ "STRING", "BOOLEAN", "INTEGER", "DOUBLE" ] }, "flags": { "type": "object", "additionalProperties": { "type": "boolean", "exampleSetFlag": false }, "exampleSetFlag": false }, "value": { "type": "string", "exampleSetFlag": false } }, "example": { "id": "string", "occuredOn": "2015-07-20T15:49:04", "valueType": "STRING", "flags": { "additionalProp1": true, "additionalProp2": true, "additionalProp3": true } }, "exampleSetFlag": true } } } } }


डीटीओ कक्षाएं उत्पन्न करते समय, हमें निम्न वर्ग संरचना मिलेगी। आप देख सकते हैं कि Enum को मूल संस्करण के रूप में संसाधित किया गया है, हालाँकि, Map<String, Boolean> प्रकार का संग्रह एक अलग वर्ग फ़्लैग में बदल गया है और संग्रह का संपूर्ण मान स्वयं फ़्लैग्स.अतिरिक्तप्रॉपर्टीज़ फ़ील्ड में आ जाएगा।


 package pckg.test; // import @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "id", "occuredOn", "valueType", "flags", "value" }) @Generated("jsonschema2pojo") public class TestEvent implements Serializable { @JsonProperty("id") private String id; @JsonProperty("occuredOn") private LocalDateTime occuredOn; @JsonProperty("valueType") private TestEvent.ValueType valueType; @JsonProperty("flags") private Flags flags; @JsonProperty("value") private String value; @JsonIgnore private Map<String, Object> additionalProperties = new LinkedHashMap<String, Object>(); private final static long serialVersionUID = 7311052418845777748L; // Getters ans Setters @Generated("jsonschema2pojo") public enum ValueType { STRING("STRING"), BOOLEAN("BOOLEAN"), INTEGER("INTEGER"), DOUBLE("DOUBLE"); private final String value; private final static Map<String, TestEvent.ValueType> CONSTANTS = new HashMap<String, TestEvent.ValueType>(); static { for (TestEvent.ValueType c: values()) { CONSTANTS.put(c.value, c); } } ValueType(String value) { this.value = value; } @Override public String toString() { return this.value; } @JsonValue public String value() { return this.value; } @JsonCreator public static TestEvent.ValueType fromValue(String value) { TestEvent.ValueType constant = CONSTANTS.get(value); if (constant == null) { throw new IllegalArgumentException(value); } else { return constant; } } } } @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ }) @Generated("jsonschema2pojo") public class Flags implements Serializable { @JsonIgnore private Map<String, Boolean> additionalProperties = new LinkedHashMap<String, Boolean>(); private final static long serialVersionUID = 7471055390730117740L; //getters and setters }

बहुरूपता

और अब देखते हैं कि बहुरूपता विकल्प कैसे प्रदान किया जाए। यह तब प्रासंगिक होता है जब हम एक ब्रोकर विषय पर कई संदेश उपप्रकार भेजना चाहते हैं और प्रत्येक उपप्रकार के लिए हमारे श्रोता को लागू करना चाहते हैं।


ऐसा करने के लिए, हमें प्रदाताओं की सूची में एक मूल वर्ग जोड़ना होगा और इसमें स्वैगर से @Schema एनोटेशन जोड़ना होगा।


 @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @Getter @Setter(AccessLevel.PROTECTED) @EqualsAndHashCode @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true, defaultImpl = ChangedEvent.class ) @JsonSubTypes(value = { @JsonSubTypes.Type(name = ChangedEvent.type, value = ChangedEvent.class), @JsonSubTypes.Type(name = DeletedEvent.type, value = DeletedEvent.class) }) @JsonIgnoreProperties(ignoreUnknown = true) @Schema(oneOf = {ChangedEvent.class, DeletedEvent.class}, discriminatorProperty = "type", discriminatorMapping = { @DiscriminatorMapping(value = ChangedEvent.type, schema = ChangedEvent.class), @DiscriminatorMapping(value = DeletedEvent.type, schema = DeletedEvent.class), }) public abstract class DomainEvent { @Schema(required = true, nullable = false) private String id; @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime occuredOn = LocalDateTime.now(); public abstract String getType(); } /** * Subtype ChangedEvent */ public class ChangedEvent extends DomainEvent implements Serializable { public static final String type = "CHANGED_EVENT"; private String valueId; private String value; } /** * Subtype DeletedEvent */ public class DeletedEvent extends DomainEvent implements Serializable { public static final String type = "DELETED_EVENT"; private String valueId; }


इस मामले में, दस्तावेज़ीकरण में घटकों का विवरण निम्नानुसार बदल जाएगा:


 "components": { "schemas": { "ChangedEvent": { "type": "object", "properties": { "id": { "type": "string", "exampleSetFlag": false }, "occuredOn": { "type": "string", "format": "date-time", "exampleSetFlag": false }, "value": { "type": "string", "exampleSetFlag": false }, "valueId": { "type": "string", "exampleSetFlag": false }, "type": { "type": "string", "exampleSetFlag": false } }, "example": { "id": "string", "occuredOn": "2015-07-20T15:49:04", "value": "string", "valueId": "string", "type": "CHANGED_EVENT" }, "exampleSetFlag": true }, "DeletedEvent": { "type": "object", "properties": { "id": { "type": "string", "exampleSetFlag": false }, "occuredOn": { "type": "string", "format": "date-time", "exampleSetFlag": false }, "valueId": { "type": "string", "exampleSetFlag": false }, "type": { "type": "string", "exampleSetFlag": false } }, "example": { "id": "string", "occuredOn": "2015-07-20T15:49:04", "valueId": "string", "type": "DELETED_EVENT" }, "exampleSetFlag": true }, "DomainEvent": { "type": "object", "properties": { "id": { "type": "string", "exampleSetFlag": false }, "occuredOn": { "type": "string", "format": "date-time", "exampleSetFlag": false }, "type": { "type": "string", "exampleSetFlag": false } }, "example": { "id": "string", "occuredOn": "2015-07-20T15:49:04", "type": "string" }, "discriminator": { "propertyName": "type", "mapping": { "CHANGED_EVENT": "#/components/schemas/ChangedEvent", "DELETED_EVENT": "#/components/schemas/DeletedEvent" } }, "exampleSetFlag": true, "oneOf": [ { "$ref": "#/components/schemas/ChangedEvent", "exampleSetFlag": false }, { "$ref": "#/components/schemas/DeletedEvent", "exampleSetFlag": false } ] } } }


उसके बाद, प्लगइन oneOf ब्लॉक और वर्णित भेदभावकर्ताओं के लिंक को ध्यान में रखेगा। नतीजतन, हमें निम्न वर्ग संरचना मिलती है।


 package pckg.test; // import @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "id", "occuredOn", "type" }) @Generated("jsonschema2pojo") @JsonTypeInfo(property = "type", use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, visible = true) @JsonSubTypes({ @JsonSubTypes.Type(name = "CHANGED_EVENT", value = ChangedEvent.class), @JsonSubTypes.Type(name = "DELETED_EVENT", value = DeletedEvent.class) }) public class DomainEvent implements Serializable { @JsonProperty("id") protected String id; @JsonProperty("occuredOn") protected LocalDateTime occuredOn; @JsonProperty("type") protected String type; @JsonIgnore protected Map<String, Object> additionalProperties = new LinkedHashMap<String, Object>(); protected final static long serialVersionUID = 4691666114019791903L; //getters and setters } // import @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "id", "occuredOn", "valueId", "type" }) @Generated("jsonschema2pojo") public class DeletedEvent extends DomainEvent implements Serializable { @JsonProperty("id") private String id; @JsonProperty("occuredOn") private LocalDateTime occuredOn; @JsonProperty("valueId") private String valueId; @JsonProperty("type") private String type; @JsonIgnore private Map<String, Object> additionalProperties = new LinkedHashMap<String, Object>(); private final static long serialVersionUID = 7326381459761013337L; // getters and setters } package pckg.test; //import @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "id", "occuredOn", "value", "type" }) @Generated("jsonschema2pojo") public class ChangedEvent extends DomainEvent implements Serializable { @JsonProperty("id") private String id; @JsonProperty("occuredOn") private LocalDateTime occuredOn; @JsonProperty("value") private String value; @JsonProperty("type") private String type; @JsonIgnore private Map<String, Object> additionalProperties = new LinkedHashMap<String, Object>(); private final static long serialVersionUID = 5446866391322866265L; //getters and setters }


प्लगइन सेटअप

प्लगइन को जोड़ने के लिए, आपको इसे gradle.build फ़ाइल में जोड़ना होगा और पैरामीटर निर्दिष्ट करना होगा:

  • फ़ोल्डर डीटीओ उत्पन्न करना था

  • नई कक्षाओं का पैकेज

  • स्प्रिंगवॉल्फ प्रलेखन URL

  • दस्तावेज़ीकरण में मूल नाम, आमतौर पर सेवा का नाम


 plugins { id 'io.github.stepanovd.springwolf2dto' version '1.0.1-alpha' } springWolfDoc2DTO{ url = 'http://localhost:8080/springwolf/docs' targetPackage = 'example.package' documentationTitle = 'my-service' targetDirectory = project.layout.getBuildDirectory().dir("generated-sources") }


बैश कमांड का उपयोग करके कार्य चलाएँ:


 ./gradle -q generateDTO

निष्कर्ष

इस लेख में, मैंने वर्णन किया है कि आप AsyncApi प्रलेखन के आधार पर नई DTO कक्षाएं उत्पन्न करने के लिए स्प्रिंगवॉल्फडॉक्स2dto प्लगइन का उपयोग कैसे कर सकते हैं। उसी समय, नई कक्षाएं मूल वंशानुक्रम के अनुसार होंगी और इसमें सही अक्रमांकन के लिए जैक्सन एनोटेशन शामिल होंगे। मुझे आशा है कि आपको यह प्लगइन आपके लिए उपयोगी लगेगा।