Wir haben den nĂŒtzlichsten Code in unserem Leben geschrieben, ihn aber in den Papierkorb geworfen. Bei uns



Ich hĂ€ngte einen Boxsack in meinen Keller, klebte ein Foto eines typischen Managers darauf und stopfte den Lautsprecher hinein, um SĂ€tze zu spielen, die mich wĂŒtend machen. Zum Beispiel sagt eine Birne: „Unternehmen brauchen keinen perfekten Code. Er muss das Problem lösen, damit der Gewinn die Kosten deckt. Wenn du dafĂŒr Govnokod brauchst, wird es Govnokod geben. “ Und ich fange an zu kuscheln.

KĂŒrzlich habe ich der Birne einen Hinweis hinzugefĂŒgt: „Typen sind schwierig und unnötig.“ In diesem Moment schlug ich so hart, dass meine Hand fast bricht. Weil genug von mir. Vor ein paar Monaten erlebte ich einen der ungeheuerlichsten FĂ€lle meiner Karriere.

Mein Freund Antokha bat mich, bei einer Lösung fĂŒr ein großes Unternehmen zu helfen. Ich stimmte zu, und wir stĂŒrzten uns in den bodenlosen Abgrund von UnternehmensabsurditĂ€t, Knirschen, Krieg mit unverstĂ€ndlichen Kollegen und allerlei Ungerechtigkeit. Wir können nichts sagen, also werden wir ĂŒber Typen sprechen, damit sich dieser MĂŒll mit niemandem wiederholt.

1


- (Antokha rcanedu ) Ich wurde mit der Entwicklung eines internen Plattform-Tools beauftragt - einer Bibliothek zum AusdrĂŒcken von Software-EntitĂ€ten in Form objektorientierter Modelle und zur einheitlichen Arbeit mit API-Diensten. Dies ist ein Tool fĂŒr die Interaktion mit Daten, die von der Quelle zur Anzeige und umgekehrt ĂŒbertragen werden.

Auf diese Weise kann eine große Menge formatiert, konvertiert, berechnet und transformiert werden. Riesige Strukturen, komplexe Hierarchien, zahlreiche Verbindungen von allem mit allem. In solchen Netzwerken ist es sehr leicht, sich zu verlaufen. Sie sehen Daten und wissen nicht, was getan werden kann und was nicht. Verbringen Sie viel Zeit, um es herauszufinden. Das macht sĂŒchtig. Nur eine gute Beschreibung der Typen löst das Problem.

Aufgrund des Mangels an geeigneter Typisierung in vielen Lösungen wird es schwieriger, zur Laufzeit und zur Kompilierungszeit dasselbe Programmverhalten zu erzielen. Typen sollten die volle Sicherheit geben, dass alles gleich ist und auch wĂ€hrend der AusfĂŒhrung geschieht. Tests können das Gleiche tun. Es ist am besten, sich auf beides zu verlassen. Wenn Sie jedoch zwischen Typen und Tests wĂ€hlen, sind Typen viel zuverlĂ€ssiger und billiger.

Wenn Sie eine Front erstellen, gibt es zwei Ursachen fĂŒr Probleme: den Benutzer und das Backend. Mit dem Benutzer ist alles einfach - es gibt viele praktische Bibliotheken und Frameworks, die von Benutzer-E / A abstrahieren (reagieren, eckig, vue und andere).

Es gibt eine andere Geschichte in Interaktion mit dem Backend. Seine Arten sind zahlreich und die Erkenntnisse sind Dunkelheit. Sie können keinen Standardansatz zur Beschreibung von Daten definieren. Aus diesem Grund wurden KrĂŒcken wie „Normalisierung der Datenstruktur“ erfunden, wenn alle eingehenden Daten auf eine starre Struktur reduziert werden und wenn etwas schief geht, Ausnahmen oder abnormale Operationen beginnen. Dies sollte die Entwicklung beschleunigen und vereinfachen, erfordert jedoch viel Dokumentation, UML-Diagramme und eine Beschreibung der Funktionen.

Ein architektonisches Problem bei der Interaktion der Client- und Serverteile trat auf, weil das Frontend ausgereift war. Es wurde ein vollwertiges GeschĂ€ft und nicht nur ein Layout ĂŒber dem Backend. Bisher haben nur die serverseitigen Entwickler die Client-Server-Interaktion abgefragt. Jetzt sind die Vorder- und RĂŒckseite gezwungen zu verhandeln und eng zusammenzuarbeiten. Wir brauchen ein Tool, mit dem wir die Arbeit mit der API von Datenquellendiensten strukturieren, den Verlust der Datenwahrheit vermeiden und gleichzeitig weitere Transformationen vereinfachen können. Dieses Problem muss von der gesamten Community angegangen werden.

- (Phil) Wenn Sie ein Back-End sind, haben Sie viele Lösungen fĂŒr Erwachsene und Methoden zur Datenmodellierung fĂŒr die Anwendung. In C # verfĂŒgen Sie beispielsweise ĂŒber eine Datenmodellklasse. Sie nehmen etwas, etwas EntityFramework, es liefert Ihnen die Attribute, mit denen Sie Ihre Modelle beschichten. Sie sagen Lib, wie sie die Basis erreichen soll. Dann verwenden Sie seine Schnittstelle zum Bearbeiten dieser Daten. Dieses Ding heißt ORM.

Im Frontend haben wir uns nicht entschieden, wie wir das am besten machen sollen - wir suchen, versuchen, schreiben lÀcherliche Artikel, dann widerlegen wir alles, fangen von vorne an und kommen immer noch nicht zu einer einzigen Entscheidung.

- (Antoha) Alles, was ich vorher geschrieben habe, hatte ein großes Problem - eine enge Spezialisierung. Jedes Mal, wenn die Bibliothek von Grund auf neu entwickelt und fĂŒr eine Art von Client-Server-Interaktion geschĂ€rft wurde. All dies geschieht aufgrund des Mangels an geeigneter Eingabe.

Ich glaube, dass es ohne statische Typisierung schwer ist, sich eine universelle Bibliothek fĂŒr die Arbeit mit APIs und DomĂ€nenausdrĂŒcken vorzustellen. Es wird viel darĂŒber nachdenken, es wird eine große Menge an Dokumentation enthalten, es wird von verschiedenen Anwendungen mit einem Hinweis auf Praktiken fĂŒr die eine oder andere Form umgeben sein. Dies ist keine Vereinfachung.

Ein gutes universelles Tool dieser Art sollte ein vollstÀndiges Bild der Daten auf jedem Slice liefern, damit Sie immer genau wissen, wie Sie mit diesen Daten arbeiten und warum sie benötigt werden.

- (Phil) Wir benötigen eine Bibliothek, die es uns ermöglicht, eine detaillierte Beschreibung und Kontrolle jeder EntitĂ€t bereitzustellen, um Daten fĂŒr diese EntitĂ€t von verschiedenen Ressourcen mit einer anderen Schnittstelle zu erhalten, von der REST-API und json-rpc bis zu graphQL und NQL. Dies ermöglicht es, die wachsende Codebasis und -struktur in Strenge und Reihenfolge zu halten. Einfach und intuitiv zu bedienen. Bereitstellung einer vollstĂ€ndigen und genauen Beschreibung des Status von Unternehmen zu jeder Zeit. Wir möchten die Module unserer Benutzer so weit wie möglich von der Datenschicht abstrahieren.

Zuerst haben wir uns die bestehende angesehen. Uns hat nichts gefallen. Aus irgendeinem Grund werden alle Bibliotheken fĂŒr die Arbeit mit Daten entweder auf js oder mit einer Reihe von Out erstellt. Diese verderben alles. Sie blenden die Entwickler aus und sagen, dass eine solche Bibliothek Ihnen nicht viel hilft, dass Sie nicht in der Lage sind, durch die Arten Ihrer Daten zu navigieren, und dass Sie ihre Verbindungen nicht ausdrĂŒcken können. Noch schlimmer wird es, wenn mehrere APIs unterschiedlichen Typs verwendet werden oder wenn sie heterogen sind.

Alle diese Bibliotheken waren durch Typen nicht ausreichend geschĂŒtzt. Daher verursachen sie mehr Probleme als sie lösen. Es ist einfacher, sie nicht zu verwenden, sondern Ihre domĂ€nenspezifischen Entscheidungen zu treffen.

Anstelle der engstirnigen Bibliothek, auf der die Aufgabe stand, haben wir uns daher entschlossen, eine viel mĂ€chtigere und abstraktere zu schaffen - fĂŒr alles geeignet. Und wir haben unglaublich an unsere Richtigkeit geglaubt, weil nur auf diese Weise wirklich gute Dinge geschaffen werden.



2


- (Antoha) Wie so oft wartete ich auf alle Arten von Zugriff, damit ich in das Firmen-Repository gelangen konnte. Und das kann mehrere Wochen dauern. In meinem Fall hat es nur einen gekostet. Zu diesem Zeitpunkt habe ich aufgrund meiner Erfahrung beim Erstellen Àhnlicher Bibliotheken die Aufgabe zerlegt und die Zeitachse geschÀtzt.

Das Problem ist, dass ich zuvor wie alle anderen sehr hoch spezialisierte Entscheidungen getroffen habe. Die Arbeit an einem universellen Werkzeug brachte zusĂ€tzliche Probleme mit sich. Das Typensystem war Ă€ußerst komplex, und weder ich noch sonst jemand hatten Erfahrung darin, dies zu entwerfen. Schlimmer noch, die Entwickler um mich herum hatten ĂŒberhaupt keine Ahnung von statischer Typisierung.

Aber ich fing an zu tun, was ich fĂŒr richtig hielt. Bei der Tageszeitung habe ich dir gesagt, was ich tue und warum, aber im Prinzip hat mich niemand verstanden. Meine Fragen an das Team bezĂŒglich des Problems sind immer unbeantwortet geblieben. Es war, als ob ich nicht existierte. Alter, der etwas sehr Kompliziertes und UnverstĂ€ndliches tut.

Ich habe verstanden, dass JavaScript hier in keiner Weise funktioniert. Ich brauchte einen PL mit einem leistungsstarken Schreibmodell, einer hervorragenden Interaktion mit JavaScript, einer großen Community und einem seriösen Ökosystem.

- (Phil) Ich habe lange darauf gewartet, dass Antoha den Charme von TypeScript versteht.

- (Antoha) Aber es gibt eine Reihe von Problemen, die dich verrĂŒckt machen. Es wird getippt, aber ich habe immer noch keine perfekte Entsprechung zwischen der AusfĂŒhrung des Programms und der AusfĂŒhrung durch den Benutzer. Typoskript scheint zunĂ€chst kompliziert. Er muss aushalten. Sie möchten immer ein Objekt oder etwas anderes nehmen und werfen. AllmĂ€hlich tauchen Sie in die Sprache ein, in ihr Typensystem, und hier beginnt etwas Interessantes zu passieren. Es wird magisch. Alles kann getippt werden.

- (Phil) Zum ersten Mal in unserem Leben haben wir uns zusammengetan und etwas begonnen.

Der erste Schritt besteht darin, dass der Benutzer das Datenschema beschreibt. Wir fragen uns, wie es aussieht. So etwas wie das:

type CustomerSchema = { 
  id: number;
  name: string;
}
const Customer = new Model<CustomerSchema>(‘Customer’);

, , . id, , , .
, . , , -. , , .

, . — - , . , , . : , , . . :

 /**
   name: String
   String -   js -: StringConstructor
              
        
*/
const customerSchema = Schema.create({
  id: Number,
  name: String,
});

. , , . , Number String . : , . :

const customerSchema = Schema.create({
  id: 1,
  name: 2,
});

. `Schema.create` , . `if (!(property instanceof String)) throw new Error(« , »)`. .

-, , -, . , . , , .

. , Schema.create.

:

//      
type Map<T> = {
  [key: string]: T | Map<T>; 
};

/**
      ,
      ,
     .
*/
type NumberType = Template<NumberConstructor, number, 'number'>;
type StringType = Template<StringConstructor, string, 'string'>;
type SymbolType = Template<SymbolConstructor, symbol, 'symbol'>;
type BooleanType = Template<BooleanConstructor, boolean, 'boolean'>;
type DateType = Template<DateConstructor, Date, 'date'>;

interface ArrayType extends Array<ExtractTypeValues<Types>> {};

type Types = {
  Number: NumberType;
  String: StringType;
  Symbol: SymbolType;
  Boolean: BooleanType;
  Date: DateType;
  Array: ArrayType;
};
//    
type MapTypes= Map<ApplyTemplate<Types>>;
//     - 
type Declaration = ExtractInputTypes<MapTypes>;
interface Schema<...> {
 //   ,   .
 create: <T extends Declaration>(
    declaration: ConvertInstanceTypesToConstructorTypes<T>
  ) => Schema<T>;
};

, . , , , , .

,

type CustomerSchema = {
  id: number;
  name: string;
};
const customerSchema: CustomerSchema = Schema.create({
  id: Number,
  name: String,
});

. , , .

. any. — any, 100%, , .

, , , . . `Schema.create` . 99% , . , . — ! , .

. , , , . . :

const customerSchema = Schema.create({
 id: Number,
 name: String,
});

//   vscode  : id, name 
Schema.raw(customerSchema). 

//   
//    .
Schema.raw(customerSchema).id;

//        .
Schema.raw(customerSchema).neId;

. :
const customerSchema = Schema.create({
 id: Number,
 name: String,
});

if (true) {
  customerSchema.add({gender: String});
} 

//        ,
//      gender.
//  ,           
// .
Schema.raw(customerSchema).

, , , . gender, , (, , this !). - . , , .

, . , , . , . .

:

const customerSchema = Schema.create({
 id: Number,
 name: String,
});

// customerSchema.add({gender: String});
//      ,    .
//  :
const customerWithGenderSchema = customerSchema.add({gender: String});

//   .
// :
Schema.raw(customerWithGenderSchema).
//  id, name, gender

// 
Schema.raw(customerSchema).
//  id, name

, , . , .

:

const customerSchema = Schema.create({
 id: Number,
 name: String,
});

const repository = RepositoryManager.create(openApiDriver, { 
 // config
});
const Customer = Model.create(repository, customerSchema);

Customer.getAll().first().
//   ide  ,       id, name  gender.
//    ,   
Customer.getAll().first().age;
//   .    ,     , 
//   .

getAll .
:

type MapSchemaToDriver<S extends Schema, D extends Driver> = 
  InferSchemaDeclaration<S> extends SchemaDeclaration
    ? InferDriverMethods<D> extends DriverTemplate<IMRReader, IMRWriter>
      ? Repository<InferSchemaDeclaration<S>, InferDriverMethods<D>>
      : never
    : never;

interface Repository<D extends Driver, S extends Schema> {
  get: <T extends DSLTerm>(dsl: ParseDSLTerm<T>) => MapSchemaToDriver<S, D> extends RepositoryTemplate ? ApplyDSLTerm<MapSchemaToDriver<S, D>, T> : Error<’type’, ‘Type error’>;
}

, :

«, B, A, , , , A. . - ».

. .

«» , , . , .

. , . :

« , *--.*, . , id . - , , ».

.



2.5


, , , . .

, — , , , . . , , .

, . , , , , . , , .

ODM ORM — IMR (Isomorphic Model Representation)


, , API . , . , select-where , .

— , , .

. . . , , , . , , , , .

, — , — , - .

.



3


— () , , . , , , . . , , , .

- , , « ». , . , — . , , ,

, .

. , , . , , , . . . , . , , , , .

— () , , . . , , , .

, , . . , .

— () , . , , . . , . - , , , , .

, - . , . , .

, , . , . .



4


— () , — . ! ?! , , , , , ODM/ORM - , . , . , , «- , ».

, , . . , , . .

, , - , . — , .

, . -:

/*
     .
    — .
       .
     
*/

import { View } from '@view';
import { Logger } from '@utils';

//   —  ,    .
//         .
import { Robot } from '@domain/models';

// ,       
//   
function foo() {
 Robot.fetch({
   location: {
     area: 2,
     free: true,
     key: 'f49a6712', //    - compile-time checked
   }
 })
 .then(View.Grid.display)
 .catch(Logger.error);
}

, . , — js/ts . , . - - , , , — Result .

. - Logger.Error, .

/*
   :
       ,
         -  .
     - 
             .
     :
*/
import { Schema, Model } from 'imr';
import { Logger } from '@utils';
import { View } from '@view';
import { robotSchema } from '@domain/schemas';
import { batteryStationService } from '@domain/infrastructure/services/batteryStation';
import { Robot } from '@domain/models';
import { batteryNode } from '../services/nodeNames';

//       
// ,       ,       ,
//  ,        «»
const robotSchemaWithBattery =
  robotSchema
    .add('battery', Schema.union('li-ion', 'silicon'))
    .remove('speed');

// ,       
//     :
function foo(nodeName) {

 //   -:   -,      
 if (nodeName === batteryNode) {
   //   ,      
   const CustomerRobot = Model.create(robotSchemaWithBattery, batteryStationService);

   CustomerRobot
     //        .
     // , , 'li-notIon'  
     .fetch({ filter: { battery: 'li-ion' } })
    
     //   .
     //   ,      ,     ,   .
     //  ,      ,
     //      ,      .
     .then(View.Grid.display)
     .catch(Logger.error)

 } else {
   Robot
     .fetch()
     .then(View.Grid.display)
     .catch(Logger.error)
 }
}



5


— () , , , - , . , - , , .

.

, , — . . , — , , . . , , — , .

, : . . .

— . , .
: rcanedu, arttom

Source: https://habr.com/ru/post/de454774/


All Articles