
Saya menggantungkan karung tinju di ruang bawah tanah saya, menempelkan foto stok manajer biasa di atasnya dan memasukkan speaker ke dalam untuk memainkan frasa yang membuat saya marah. Misalnya, buah pir berkata: βBisnis tidak membutuhkan kode sempurna Anda. Dia perlu memecahkan masalah sehingga keuntungan menutupi biaya. Jika Anda membutuhkan govnokod untuk ini, maka akan ada govnokod. " Dan saya mulai berpelukan.
Baru-baru ini, saya menambahkan catatan pada pir: "Jenis sulit dan tidak perlu." Pada saat ini, saya memukul dengan keras sehingga tangan saya hampir pecah. Karena saya cukup. Beberapa bulan yang lalu, saya mengalami salah satu kasus paling mengerikan dalam karir saya.
Teman saya Antokha meminta saya untuk membantu dengan solusi untuk satu perusahaan besar. Saya setuju, dan kami terjun ke dalam jurang maut yang absurd, korporasi, perang dengan rekan-rekan yang tidak mengerti apa-apa dan segala macam ketidakadilan. Kami tidak bisa mengatakan apa-apa, jadi kami akan berbicara tentang jenis sehingga sampah seperti itu tidak akan terulang dengan siapa pun.
1
- (Antokha rcanedu ) Saya dipercayakan dengan pengembangan alat platform internal - perpustakaan untuk mengekspresikan entitas perangkat lunak dalam bentuk model berorientasi objek dan untuk pekerjaan seragam dengan layanan API. Yaitu, alat untuk berinteraksi dengan data yang bergerak dari sumber ke tampilan dan sebaliknya.
Dengan cara ini, sejumlah besar pemformatan, konversi, penghitungan dan transformasi. Struktur besar, hierarki yang kompleks, banyak koneksi dari segalanya dengan segalanya. Dalam jaringan seperti itu sangat mudah tersesat. Anda melihat potongan data dan tidak tahu apa yang bisa dilakukan dan apa yang tidak bisa dilakukan. Luangkan banyak waktu untuk mencari tahu. Ini sangat adiktif. Hanya deskripsi yang baik tentang tipe yang memecahkan masalah.
Karena kurangnya pengetikan yang sesuai dalam banyak solusi, menjadi lebih sulit untuk mencapai perilaku program yang sama pada saat runtime dan pada waktu kompilasi. Jenis harus memberikan keyakinan penuh bahwa semuanya sama dan juga akan terjadi selama eksekusi. Tes dapat melakukan hal yang sama. Yang terbaik adalah mengandalkan keduanya. Tetapi jika Anda memilih antara jenis dan tes - jenis jauh lebih dapat diandalkan dan lebih murah.
Saat Anda membuat garis depan, ada dua sumber masalah - pengguna dan backend. Semuanya sederhana dengan pengguna - ada banyak perpustakaan yang nyaman dan kerangka kerja abstrak dari pengguna I / O (reaksi, sudut, sudut dan lain-lain).
Ada cerita lain dalam interaksi dengan backend. Spesiesnya banyak, dan realisasinya gelap. Anda tidak dapat menentukan satu pendekatan standar untuk menggambarkan data. Karena itu, kruk seperti "normalisasi struktur data" diciptakan, ketika semua data yang masuk direduksi menjadi struktur yang kaku, dan jika terjadi kesalahan, pengecualian atau operasi abnormal dimulai. Ini harus mempercepat dan menyederhanakan pengembangan, tetapi sebenarnya membutuhkan banyak dokumentasi, diagram UML, deskripsi fitur.
Masalah arsitektur dalam interaksi bagian klien dan server muncul karena frontend telah matang. Itu menjadi bisnis penuh, dan bukan hanya tata letak di atas backend. Sebelumnya, hanya pengembang sisi server yang meminta interaksi klien-server. Sekarang garis depan dan belakang dipaksa untuk bernegosiasi dan bekerja sama. Kami membutuhkan alat yang akan memungkinkan kami untuk menyusun pekerjaan dengan layanan sumber data API, untuk menghindari hilangnya kebenaran data, dan pada saat yang sama menyederhanakan transformasi lebih lanjut. Masalah ini harus diatasi oleh seluruh komunitas.
- (Phil) Jika Anda seorang back-end, Anda memiliki banyak solusi dewasa dan praktik pemodelan data untuk aplikasi tersebut. Misalnya, dalam C # Anda menampilkan kelas model data. Anda mengambil sesuatu, beberapa EntityFramework, itu memberi Anda atribut dengan mana Anda melapisi model Anda. Anda memberi tahu Lib cara mencapai pangkalan. Kemudian Anda menggunakan antarmuka untuk memanipulasi data ini. Benda ini disebut ORM.
Di front-end, kami tidak memutuskan cara terbaik untuk melakukannya - kami mencari, mencoba, menulis artikel yang konyol, kemudian kami menyangkal semuanya, kami memulai kembali dan kami masih tidak akan mengambil satu keputusan.
- (Antoha) Semua yang saya tulis sebelumnya memiliki satu masalah besar - spesialisasi sempit. Setiap kali perpustakaan dikembangkan dari awal dan setiap kali dipertajam untuk satu jenis interaksi klien-server. Semua ini terjadi karena kurangnya pengetikan yang cocok.
Saya percaya bahwa tanpa mengetik statis sulit membayangkan perpustakaan universal untuk bekerja dengan API dan ekspresi domain. Akan ada banyak refleksi di dalamnya, itu akan berisi sejumlah besar dokumentasi, itu akan dikelilingi oleh aplikasi yang berbeda dengan indikasi praktik untuk satu atau bentuk lainnya. Ini bukan penyederhanaan.
Alat universal yang baik dari jenis ini harus memberikan gambaran lengkap tentang data pada setiap irisan, sehingga Anda selalu tahu persis bagaimana bekerja dengan data ini dan mengapa itu diperlukan.
- (Phil) Kami membutuhkan lib yang akan memungkinkan kami untuk memberikan deskripsi dan kontrol terperinci dari setiap entitas, untuk mendapatkan data untuk entitas ini dari sumber daya yang berbeda dengan antarmuka yang berbeda, dari API REST dan json-rpc ke graphQL dan NQL. Yang akan memungkinkan untuk menjaga basis dan struktur kode yang tumbuh dalam ketelitian dan ketertiban. Sederhana dan intuitif untuk digunakan. Memberikan deskripsi yang lengkap dan akurat tentang status entitas setiap saat. Kami ingin mengabstraksi modul pengguna kami dari lapisan data sebanyak mungkin.
Pertama-tama, kami melihat yang sudah ada. Kami tidak menyukai apa pun. Untuk beberapa alasan, semua perpustakaan untuk bekerja dengan data dibuat baik pada js, atau dengan banyak yang keluar. Ini semua merusak segalanya. Mereka menutup mata kepada pengembang, mengatakan bahwa perpustakaan seperti itu tidak akan banyak membantu Anda, bahwa Anda tidak akan dapat menavigasi tipe data Anda, Anda tidak akan dapat mengekspresikan koneksi mereka. Hal-hal menjadi lebih buruk ketika banyak API dari jenis yang berbeda digunakan atau heterogen.
Semua perpustakaan ini tidak cukup dilindungi oleh tipe. Karena itu, mereka menciptakan lebih banyak masalah daripada yang mereka pecahkan. Lebih mudah untuk tidak menggunakannya, tetapi untuk membuat keputusan khusus domain Anda.
Oleh karena itu, alih-alih pikiran yang sempit di mana tugas itu berdiri, kami memutuskan untuk membuat yang lebih kuat dan abstrak - cocok untuk semuanya. Dan kami sangat percaya pada kebenaran kami, karena hanya dengan cara ini benar-benar hal-hal baik diciptakan.
2
- (Antoha) Seperti yang sering terjadi, saya menunggu semua jenis akses sehingga saya akan diizinkan masuk ke dalam repositori perusahaan. Dan ini bisa berlangsung selama beberapa minggu. Dalam kasus saya, hanya butuh satu. Pada saat ini, berdasarkan pengalaman saya dalam membuat perpustakaan yang sama, saya mendekomposisi tugas, memperkirakan timeline.
Masalahnya adalah bahwa sebelumnya, seperti orang lain, saya membuat keputusan yang sangat terspesialisasi. Bekerja pada alat universal memerlukan masalah tambahan. Sistem tipe keluar sangat kompleks, dan baik saya maupun orang lain tidak memiliki pengalaman dalam mendesain ini. Lebih buruk lagi, para pengembang di sekitar saya tidak tahu sama sekali mengetik statis.
Tetapi saya mulai melakukan apa yang menurut saya benar. Di Daily, saya memberi tahu Anda apa yang saya lakukan dan mengapa, tetapi, pada prinsipnya, tidak ada yang mengerti saya. Pertanyaan saya kepada tim tentang masalah selalu tetap tidak terjawab. Seolah-olah saya tidak ada. Bung yang melakukan sesuatu yang sangat rumit dan tidak bisa dipahami.
Saya mengerti bahwa javaScript tidak akan berfungsi di sini dengan cara apa pun. Saya membutuhkan PL dengan model pengetikan yang kuat, interaksi yang sangat baik dengan javaScript, memiliki komunitas besar dan ekosistem yang serius.
- (Phil) Saya telah menunggu lama untuk Antoha untuk memahami pesona typeScript.
- (Antoha) Tetapi ada sejumlah masalah di dalamnya yang membuat Anda gila. Ada pengetikan, tapi saya masih belum memiliki korespondensi yang sempurna antara eksekusi program dan eksekusi oleh pengguna. Script tampaknya rumit pada awalnya. Dia harus bertahan. Anda selalu ingin mengambil dan melempar benda atau apapun. Secara bertahap Anda mempelajari bahasa, ke sistem tipenya, dan di sini hal yang menarik mulai terjadi. Itu menjadi ajaib. Semuanya bisa diketik.
- (Phil) Untuk pertama kalinya dalam hidup kami, kami berkumpul dan memulai sesuatu.
Langkah pertama adalah bagi pengguna untuk menggambarkan skema data. Kami bertanya-tanya bagaimana tampilannya. Sesuatu seperti ini:
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