我们编写了生命中最有用的代码,但将其扔进了垃圾箱。 和我们一起



我在地下室里挂了一个出气筒,在上面贴着一位典型经理的照片,然后把演讲者塞进里面玩一些让我生气的短语。 例如,一个梨说:“业务不需要您的完美代码。 他需要解决问题,以使利润涵盖成本。 如果您需要govnokod,那么就会有govnokod。” 我开始拥抱。

最近,我在梨子上加了一条字:“类型很困难,没有必要。” 这时,我打得很厉害,手几乎断了。 因为我足够了。 几个月前,我经历了职业生涯中最严重的案例之一。

我的朋友安托卡(Antokha)要求我为一家大型公司提供解决方案。 我同意了,我们陷入了公司荒谬,紧缩,与不可理解的同事之间的战争以及各种不公正的深渊。 我们什么也不能说,因此我们将讨论类型,以使此类垃圾永远不会重复出现。

1个


-(Antokha rcanedu我受委托开发了一个内部平台工具-一个以面向对象模型的形式表示软件实体并与API服务统一工作的库。 即,一种用于与从源传播到显示器,反之亦然的数据进行交互的工具。

这样,大量的格式化,转换,计算和转换。 庞大的结构,复杂的层次结构,万物之间的无数联系。 在这样的网络中,很容易迷路。 您看到的是数据,却不知道可以做什么和不能做什么。 花很多时间来解决它。 这是很容易上瘾的。 只有很好地描述类型才能解决问题。

由于许多解决方案中缺少合适的类型,因此在运行时和编译时实现相同的程序行为变得更加困难。 类型应该完全确保所有内容都是相同的,并且在执行期间也会发生。 测试可以做同样的事情。 最好两者都依靠。 但是,如果您在类型和测试之间进行选择-类型会更可靠,更便宜。

当您成为前台时,有两个问题的根源-用户和后端。 对用户而言,一切都很简单-有许多方便的库和框架从用户I / O中抽象出来(反应,角度,Vue等)。

与后端交互还有另一个故事。 它的种类很多,而实现却是黑暗。 您无法定义一种描述数据的标准方法。 因此,发明了诸如“数据结构规范化”之类的拐杖,当所有传入数据都简化为刚性结构时,如果出现问题,则异常或异常操作就会开始。 这样可以加快并简化开发,但是实际上,它需要大量文档,UML图和功能描述。

由于前端已经成熟,因此在客户端和服务器部分的交互中出现了体系结构问题。 它变成了成熟的业务,而不仅仅是后端之上的布局。 以前,只有服务器端开发人员要求客户端-服务器交互。 现在,正反双方被迫谈判和密切合作。 我们需要一个工具,该工具将使我们能够与API数据源服务一起构建工作,避免丢失数据真实性,同时简化进一步的转换。 整个社区必须解决这个问题。

-(Phil)如果您是后端用户,则有很多针对该应用程序的成人解决方案和数据建模实践。 例如,在C#中,您具有数据模型类。 您需要一些EntityFramework,它为您提供了用来覆盖模型的属性。 您告诉Lib如何到达基地。 然后,您可以使用其界面来处理此数据。 这个东西叫做ORM。

在前端,我们并没有决定如何做到最好-我们搜索,尝试,撰写荒谬的文章,然后我们驳斥一切,重新开始,我们仍然不会做出一个决定。

-(Antoha)我之前写的所有内容都有一个大问题-狭义的专业化。 每次从头开始开发该库,并且每次针对一种客户端-服务器交互来对其进行优化。 所有这些都是由于缺少适当的键入而发生的。

我相信,如果没有静态输入,就很难想象有一个通用的库可以使用API​​和域表达式。 其中会有很多反思,它将包含大量文档,并且将被不同的应用程序围绕,并指明一种或另一种形式的实践。 这不是简化。

一个好的通用工具应该可以在任何切片上提供完整的数据图,以便您始终准确地知道如何使用该数据以及为什么需要它。

-(Phil)我们需要一个库,使我们能够为每个实体提供详细的描述和控制,以便从具有不同接口的不同资源(从REST API和json-rpc到graphQL和NQL)获取该实体的数据。 这将使不断增长的代码库和结构保持严格和有序。 使用简单直观。 随时提供完整,准确的实体状态描述。 我们想尽可能地从数据层抽象用户的模块。

首先,我们看一下现有的。 我们什么都不喜欢。 出于某种原因,所有用于处理数据的库都是在js上创建的,或者都是由js制成的。 这些都破坏了一切。 他们对开发人员视而不见,说这样的库对您没有多大帮助,您将无法浏览数据的类型,也将无法表达他们的联系。 当使用不同类型的多个API或异构API时,情况会变得更糟。

所有这些库都没有受到类型的充分保护。 因此,它们产生的问题多于解决的问题。 不使用它们,而是根据您的特定领域做出决定会更容易。

因此,我们决定创建一个更强大,更抽象的库-适用于所有事物,而不是紧握任务的狭lib库,并且我们坚信自己的正确性,因为只有这样,才能创造出真正的好东西。



2


-(Antoha)通常,我正在等待各种访问权限,这样我就可以进入公司资源库。 这可能持续数周。 就我而言,只用了一个。 这时,根据我在创建类似库中的经验,我分解了任务,估计了时间表。

问题是,在像其他所有人一样,我做出非常专业的决定之前。 使用通用工具会带来其他问题。 类型系统非常复杂,我和其他任何人都没有设计经验。 更糟糕的是,我周围的开发人员根本不了解静态类型。

但是我开始做我认为正确的事。 在日报上,我告诉您我在做什么以及为什么这样做,但是原则上没有人了解我。 我向团队提出的有关该问题的问题始终没有得到解答。 好像我不存在。 杜德(Dude),他做的事情非常复杂和难以理解。

我知道javaScript不能以任何方式在这里工作。 我需要一个具有强大的输入模型,与javaScript的出色交互,具有大型社区和严肃的生态系统的PL。

-(Phil)我已经等了很长时间了,Antoha才了解TypeScript的魅力。

-(Antoha)但是其中有许多问题使您发疯。 有输入,但是在程序的执行和用户的执行之间仍然没有完美的对应关系。 一开始打字稿似乎很复杂。 他必须忍受。 您总是想拿东西或扔东西。 逐渐地,您深入研究了语言,进入了语言的类型系统,在这里开始发生了一件有趣的事情。 它变得神奇。 一切都可以键入。

-(Phil)我们有生以来第一次在一起,开始了一些事情。

第一步是让用户描述数据方案。 我们想知道它的外观。 像这样:

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/zh-CN454774/


All Articles