Bagaimana kami memodifikasi produk untuk klien tertentu

gambar

Jadi, kami menjual klien produk perangkat lunak B2B.

Pada presentasi, dia menyukai semuanya, tetapi selama implementasi ternyata ada sesuatu yang tidak cocok. Tentu saja Anda dapat mengatakan bahwa Anda harus mengikuti "praktik terbaik", dan mengubah diri Anda menjadi suatu produk, dan bukan sebaliknya. Ini dapat berfungsi jika Anda memiliki merek yang kuat (misalnya, tiga huruf besar, dan Anda dapat mengirim ketiga huruf kecil). Jika tidak, mereka akan dengan cepat menjelaskan kepada Anda bahwa pelanggan mencapai segalanya berkat proses bisnisnya yang unik, dan mari kita ganti produk Anda dengan lebih baik atau tidak akan berhasil. Ada opsi untuk menolak dan merujuk pada kenyataan bahwa lisensi telah dibeli, dan tidak ada tempat untuk pergi dari kapal selam. Tetapi di pasar yang relatif sempit, strategi seperti itu tidak akan bekerja untuk waktu yang lama.

Kami harus memodifikasi.

Pendekatannya


Ada beberapa pendekatan dasar untuk adaptasi produk.

Monolith


Setiap perubahan dilakukan langsung ke kode sumber produk, tetapi termasuk dalam opsi tertentu. Dalam produk-produk tersebut, sebagai suatu peraturan, ada bentuk-bentuk mengerikan dengan pengaturan, yang, agar tidak menjadi bingung, diberi nomor atau kodenya. Kerugian dari pendekatan ini adalah bahwa kode sumber berubah menjadi spageti besar, di mana ada begitu banyak kasus penggunaan yang berbeda sehingga menjadi sangat panjang dan mahal untuk dipelihara. Setiap opsi selanjutnya membutuhkan lebih banyak sumber daya. Kinerja produk semacam itu juga menyisakan banyak hal yang diinginkan. Dan jika bahasa di mana produk ditulis tidak mendukung praktik modern seperti warisan dan polimorfisme, maka semuanya menjadi sangat sedih.

Salin


Klien diberi seluruh kode sumber produk dengan lisensi untuk memodifikasinya. Seringkali, vendor tersebut memberi tahu pelanggan bahwa mereka tidak akan mengadaptasi produk itu sendiri, karena akan terlalu mahal (jauh lebih menguntungkan bagi vendor untuk menjual lisensi daripada menghubungi layanan). Tetapi mereka memiliki agen outsourcing yang akrab yang mempekerjakan karyawan yang relatif murah dan berkualitas tinggi di suatu tempat di negara ketiga yang siap membantu mereka. Ada juga situasi di mana perbaikan akan dilakukan langsung oleh spesialis pelanggan (jika mereka memiliki unit kepegawaian). Dalam kasus seperti itu, kode sumber diambil sebagai titik awal, dan kode yang dimodifikasi tidak akan memiliki koneksi dengan apa yang semula, dan akan menjalani hidupnya sendiri. Dalam hal ini, Anda dapat dengan aman menghapus setidaknya setengah dari produk asli dan menggantinya dengan logika Anda sendiri.

Penggabungan


Ini adalah campuran dari dua pendekatan pertama. Tetapi di dalamnya, pengembang yang memperbaiki kode harus selalu ingat: "penggabungan akan datang". Ketika versi baru dari produk sumber dirilis, ia harus dalam kebanyakan kasus secara manual menggabungkan perubahan dalam sumber dan kode yang dimodifikasi. Masalahnya adalah bahwa dalam setiap konflik akan perlu untuk mengingat mengapa perubahan tertentu dibuat, dan ini bisa sangat lama. Dan jika kode refactoring dilakukan dalam produk asli (misalnya, blok kode hanya disusun ulang), maka penggabungan akan sangat memakan waktu.

Modularitas


Secara logis pendekatan yang paling benar. Kode sumber produk dalam hal ini tidak mengalami perubahan, dan modul tambahan ditambahkan yang memperluas fungsionalitas. Namun, untuk mengimplementasikan skema semacam itu, produk harus memiliki arsitektur yang memungkinkannya diperluas dengan cara ini.

Deskripsi


Selanjutnya dengan contoh saya akan menunjukkan bagaimana kami memperluas produk yang dikembangkan berdasarkan platform lsFusion terbuka dan gratis.

Elemen kunci dari sistem adalah modul. Modul adalah file teks dengan ekstensi lsf , yang berisi kode lsFusion . Dalam setiap modul, baik logika domain (fungsi, kelas, tindakan) dan logika presentasi (formulir, navigator) dideklarasikan. Modul, sebagai aturan, terletak di direktori, dibagi dengan prinsip logis. Suatu produk adalah kumpulan modul yang mengimplementasikan fungsinya dan disimpan dalam repositori terpisah.

Modul saling tergantung. Satu modul tergantung pada yang lain jika menggunakan logikanya (misalnya, merujuk ke properti atau formulir).

Ketika klien baru muncul, repositori terpisah (Git atau Subversion) diluncurkan untuk itu, di mana modul dengan modifikasi yang diperlukan akan dibuat. Repositori ini mendefinisikan apa yang disebut modul teratas. Ketika server mulai, hanya modul-modul yang akan terhubung, yang bergantung langsung atau transitif melalui modul lain. Ini memungkinkan klien untuk menggunakan tidak semua fungsi produk, tetapi hanya bagian yang ia butuhkan.
Jenkins membuat tugas yang menggabungkan modul produk dan klien ke dalam satu file jar, yang kemudian diinstal pada server produksi atau pengujian.

Pertimbangkan beberapa kasus utama peningkatan yang muncul dalam praktik:

Misalkan kita memiliki modul Pesanan dalam produk, yang menggambarkan logika pesanan standar:

Modul pemesanan
MODULE Order;

CLASS Book '' ;
name '' = DATA ISTRING [ 100 ] (Book) IN id;

CLASS Order '' ;
date '' = DATA DATE (Order) IN id;
number '' = DATA STRING [ 10 ] (Order) IN id;

CLASS OrderDetail ' ' ;
order '' = DATA Order (OrderDetail) NONULL DELETE ;

book '' = DATA Book (OrderDetail) NONULL ;
nameBook '' (OrderDetail d) = name(book(d));

quantity '' = DATA INTEGER (OrderDetail);
price '' = DATA NUMERIC [ 14 , 2 ] (OrderDetail);
sum '' (OrderDetail d) = quantity(d) * price(d);

FORM order ''
OBJECTS o = Order PANEL
PROPERTIES (o) date, number

OBJECTS d = OrderDetail
PROPERTIES (d) nameBook, quantity, price, NEW , DELETE
FILTERS order(d) = o

EDIT Order OBJECT o
;

FORM orders ''
OBJECTS o = Order
PROPERTIES (o) READONLY date, number
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

NAVIGATOR {
NEW orders;
}

Pelanggan X ingin menambahkan persentase diskon dan harga diskon untuk garis pesanan.
Pertama, modul OrderX baru dibuat di repositori pelanggan. Di headernya, dependensi ditempatkan pada modul Order asli:
REQUIRE Order;

Dalam modul ini, kami mendeklarasikan properti baru di mana bidang tambahan akan dibuat dalam tabel, dan menambahkannya ke formulir:
discount ', %' = DATA NUMERIC [ 5 , 2 ] (OrderDetail);
discountPrice ' ' = DATA NUMERIC [ 14 , 2 ] (OrderDetail);

EXTEND FORM order
PROPERTIES (d) AFTER price(d) discount, discountPrice READONLY
;

Kami membuat harga diskon tidak tersedia untuk direkam. Ini akan dihitung sebagai peristiwa terpisah ketika harga awal atau persentase diskon berubah:
WHEN LOCAL CHANGED (price(OrderDetail d)) OR CHANGED (discount(d)) DO
discountPrice(d) <- price(d) * ( 100 (-) discount(d)) / 100 ;

Sekarang Anda perlu mengubah perhitungan jumlah pada baris pesanan (harus memperhitungkan harga diskon kami yang baru dibuat). Untuk melakukan ini, kami biasanya membuat "titik masuk" tertentu di mana modul lain dapat menyisipkan perilaku mereka. Alih-alih deklarasi awal dari properti penjumlahan dalam modul Pesanan, kami menggunakan yang berikut ini:
sum '' = ABSTRACT CASE NUMERIC [ 16 , 2 ] (OrderDetail);
sum (OrderDetail d) += WHEN price(d) THEN quantity(d) * price(d);

Dalam hal ini, nilai properti penjumlahan akan dikumpulkan dalam satu KASUS, di mana KAPAN dapat tersebar di berbagai modul. Dijamin bahwa jika modul A tergantung pada modul B, maka semua KAPAN modul B akan bekerja lebih lambat dari KAPAN modul A. Untuk menghitung dengan benar jumlah diskon, deklarasi berikut ditambahkan ke modul OrderX :
sum(OrderDetail d) += WHEN discount(d) THEN quantity(d) * discountPrice(d);

Akibatnya, jika diskon ditetapkan, jumlahnya akan dikenakan padanya, jika tidak, ekspresi asli.

Misalkan klien ingin menambahkan batasan bahwa jumlah pesanan tidak boleh melebihi jumlah tertentu. Dalam modul OrderX yang sama, kami mendeklarasikan properti tempat nilai batas akan disimpan, dan menambahkannya ke formulir opsi standar (Anda dapat membuat formulir terpisah dengan pengaturan jika Anda mau):
orderLimit ' ' = DATA NUMERIC [ 16 , 2 ] ();
EXTEND FORM options
PROPERTIES () orderLimit
;

Kemudian, dalam modul yang sama, kami mendeklarasikan jumlah pesanan, menunjukkannya pada formulir dan menambahkan batas kelebihannya:
sum '' (Order o) = GROUP SUM sum(OrderDetail d) IF order(d) = o;
EXTEND FORM order
PROPERTIES (o) sum
;
CONSTRAINT sum(Order o) > orderLimit() MESSAGE ' ' ;

Dan akhirnya, klien diminta untuk sedikit mengubah desain formulir pengeditan pesanan: untuk membuat tajuk pesanan di sebelah kiri garis dengan pemisah, serta untuk selalu menunjukkan harga dengan akurasi dua karakter. Untuk melakukan ini, kode berikut ditambahkan ke modulnya, yang mengubah desain standar yang dihasilkan dari formulir pemesanan:
DESIGN order {
OBJECTS {
NEW pane {
fill = 1 ;
type = SPLITH ;
MOVE BOX (o);
MOVE BOX (d) {
PROPERTY (price(d)) { pattern = '#,##0.00' ; }
PROPERTY (discountPrice(d)) { pattern = '#,##0.00' ; }
}
}
}
}
Sebagai hasilnya, kami mendapatkan dua modul Order (dalam produk), di mana logika dasar pesanan diimplementasikan, dan OrderX (di pelanggan), di mana logika diskon yang diperlukan diimplementasikan:

Memesan
MODULE Order;

CLASS Book '' ;
name '' = DATA ISTRING [ 100 ] (Book) IN id;

CLASS Order '' ;
date '' = DATA DATE (Order) IN id;
number '' = DATA STRING [ 10 ] (Order) IN id;

CLASS OrderDetail ' ' ;
order '' = DATA Order (OrderDetail) NONULL DELETE ;

book '' = DATA Book (OrderDetail) NONULL ;
nameBook '' (OrderDetail d) = name(book(d));

quantity '' = DATA INTEGER (OrderDetail);
price '' = DATA NUMERIC [ 14 , 2 ] (OrderDetail);
sum '' = ABSTRACT CASE NUMERIC [ 16 , 2 ] (OrderDetail);
sum (OrderDetail d) += WHEN price(d) THEN quantity(d) * price(d);

FORM order ''
OBJECTS o = Order PANEL
PROPERTIES (o) date, number

OBJECTS d = OrderDetail
PROPERTIES (d) nameBook, quantity, price, NEW , DELETE
FILTERS order(d) = o

EDIT Order OBJECT o
;

FORM orders ''
OBJECTS o = Order
PROPERTIES (o) READONLY date, number
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

NAVIGATOR {
NEW orders;
}

Pesananx
MODULE OrderX;

REQUIRE Order;

discount ', %' = DATA NUMERIC [ 5 , 2 ] (OrderDetail);
discountPrice ' ' = DATA NUMERIC [ 14 , 2 ] (OrderDetail);

EXTEND FORM order
PROPERTIES (d) AFTER price(d) discount, discountPrice READONLY
;

WHEN LOCAL CHANGED (price(OrderDetail d)) OR CHANGED (discount(d)) DO
discountPrice(d) <- price(d) * ( 100 (-) discount(d)) / 100 ;

sum(OrderDetail d) += WHEN discount(d) THEN quantity(d) * discountPrice(d);

orderLimit ' ' = DATA NUMERIC [ 16 , 2 ] ();
EXTEND FORM options
PROPERTIES () orderLimit
;

sum '' (Order o) = GROUP SUM sum(OrderDetail d) IF order(d) = o;
EXTEND FORM order
PROPERTIES (o) sum
;
CONSTRAINT sum(Order o) > orderLimit() MESSAGE ' ' ;

DESIGN order {
OBJECTS {
NEW pane {
fill = 1 ;
type = SPLITH ;
MOVE BOX (o);
MOVE BOX (d) {
PROPERTY (price(d)) { pattern = '#,##0.00' ; }
PROPERTY (discountPrice(d)) { pattern = '#,##0.00' ; }
}
}
}
}

Perlu dicatat bahwa modul OrderX dapat disebut OrderDiscount dan ditransfer langsung ke produk. Kemudian, jika perlu, akan memungkinkan bagi setiap pelanggan untuk dengan mudah menghubungkan fungsionalitas dengan diskon.

Ini jauh dari semua kemungkinan yang disediakan platform untuk memperluas fungsionalitas dalam modul individual. Misalnya, menggunakan pewarisan, Anda dapat mengimplementasikan logika register secara modular.

Jika ada perubahan dalam kode sumber produk yang bertentangan dengan kode dalam modul dependen, kesalahan akan dihasilkan ketika server mulai. Misalnya, jika formulir pemesanan dihapus dalam modul Pesanan , maka pada saat mulai akan ada kesalahan bahwa formulir pemesanan tidak ditemukan dalam modul OrderX . Juga, kesalahan akan disorot dalam IDE . Selain itu, IDE memiliki fungsi untuk mencari semua kesalahan dalam proyek, yang memungkinkan Anda untuk mengidentifikasi semua masalah yang terjadi karena memperbarui versi produk.

Dalam praktiknya, kami memiliki semua repositori (dari produk dan semua pelanggan) yang terhubung ke proyek yang sama, jadi kami dengan tenang mereformasi produk, sambil mengubah logika dalam modul pelanggan di mana ia digunakan.

Kesimpulan


Arsitektur mikromodular tersebut memberikan manfaat berikut:

  • Setiap pelanggan hanya terhubung fungsi yang dia butuhkan . Struktur basis datanya hanya berisi bidang-bidang yang ia gunakan. Antarmuka solusi akhir tidak mengandung elemen yang tidak perlu. Server dan klien tidak melakukan acara dan pemeriksaan yang tidak perlu.
  • Fleksibilitas dalam perubahan fungsionalitas dasar . Langsung di proyek klien, Anda dapat membuat perubahan pada segala bentuk produk, menambahkan acara, objek dan properti baru, tindakan, mengubah desain, dan banyak lagi.
  • Pengiriman perbaikan baru yang dibutuhkan oleh pelanggan dipercepat secara signifikan . Dengan setiap permintaan perubahan, Anda tidak perlu memikirkan bagaimana itu akan memengaruhi pelanggan lain. Karena hal ini, banyak perbaikan dapat dilakukan dan dioperasikan sesegera mungkin (seringkali dalam beberapa jam).
  • Skema yang lebih nyaman untuk memperluas fungsionalitas produk . Pertama, fungsionalitas apa pun dapat dimasukkan untuk pelanggan tertentu yang siap untuk mencobanya, dan kemudian, jika implementasi yang sukses, modul-modul tersebut sepenuhnya ditransfer ke gudang produk.
  • Kemandirian basis kode . Karena banyak perbaikan disediakan di bawah kontrak layanan pelanggan, secara resmi, seluruh kode yang dikembangkan di bawah kontrak ini adalah milik pelanggan. Dengan skema ini, dipastikan pemisahan kode produk yang dimiliki oleh vendor dari kode yang dimiliki oleh klien. Atas permintaan, kami mentransfer repositori ke server klien, tempat ia dapat memodifikasi fungsionalitas yang ia butuhkan dengan pengembangnya sendiri. Selain itu, jika pemasok melisensikan modul produk individual, pelanggan tidak memiliki kode sumber modul yang tidak ada lisensi. Dengan demikian, ia tidak memiliki kemampuan teknis untuk menghubungkan mereka secara independen yang melanggar ketentuan perizinan.

Skema modularitas yang dijelaskan di atas dengan bantuan ekstensi dalam pemrograman paling sering disebut mix in . Sebagai contoh, Microsoft Dynamics baru-baru ini memperkenalkan konsep ekstensi, yang juga memungkinkan Anda untuk memperluas modul dasar. Namun, banyak pemrograman tingkat rendah diperlukan di sana, yang pada gilirannya membutuhkan kualifikasi pengembang yang lebih tinggi. Selain itu, tidak seperti lsFusion, perluasan acara dan pembatasan membutuhkan "titik masuk" awal ke produk untuk memanfaatkan ini.

Saat ini, sesuai dengan skema yang dijelaskan di atas, kami mendukung dan menerapkan sistem ERP untuk ritel dengan lebih dari 30 pelanggan yang relatif besar, yang terdiri dari lebih dari 1000 modul. Di antara pelanggan ada jaringan FMCG, serta apotek, toko pakaian, toko rantai drogerie, grosir dan lain-lain. Dalam produk, masing-masing, ada kategori terpisah dari modul yang terhubung tergantung pada industri dan proses bisnis yang digunakan.

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


All Articles