خانه / ٌWeb Service / Richardson Maturity Model

Richardson Maturity Model

REpresentational State Transfer یا REST عبارتی است که نخستین بار توسط آقای Roy Fielding در سال 2000 و در تز دکترای خود با عنوان Design of Network-based Software Architectures معرفی شد. این سبک معماری اشاره به یکسری ویژگی‌ها و محدودیت‌هایی دارد که جهت رسیدن یک معماری توزیع شده‌‎ی بر پایه HTTP مورد نیاز می‌باشد.

مدل تکاملی Richardson Maturity Model یا  RMM جهت توصیف ویژگی های اصلی یک سرویس RESTful و چگونگی تکامل و رسیدن از سرویس های مرسوم Remote-Procedure Call(RPC) به سرویس‌هایی که به اصطلاح RESTful نامیده می‌شوند توسط لئونارد ریچاردسون ارائه شده است. متن زیر مقاله ی آقای Martin Fowler در توضیح این مدل می باشد که به خوبی با ارئه ی یک مثال کاربردی نوبت دهی، جنبه های مختلف این مدل را مورد بررسی قرار داده است.

Richardson Maturity Model

گام هایی به سمت محبوبیتREST

مدلی(توسعه داده شده توسط لئونارد ریچاردسون) که عناصر اصلی یک رویکرد REST رو به سه گام می شکند. این مدل resource ها, http verb ها و hypermedia controller ها رو معرفی می کنه.

اخیرا پیش نویس Rest In Practice را مطالعه کردم؛ کتابی که چند نفر از همکارانم بر روی آن کار کردند. کمک اصلی آنها توضیح این است که به چه صورت وب سرویس های REST را جهت کنترل بسیاری از مشکلات یکپارچه سازی که سازمان ها با آنها مواجه می باشند؛ باید استفاده کرد. قسمت اصلی کتاب شامل این مفهوم است که وب، یک سیستم توزیع شده ی انبوه است که بخوبی کار می کند, و ما قادریم جهت ایجاد ساده تر سیستم های یکپارچه شده از آن ایده بگیریم.


 Figure 1: Steps toward REST

جهت کمک به توضیح ویژگی های خاص یک سیستم وب؛ نویسنده ها از یک مدل بلوغ سیستم های restful که توسط Leonard Richardson توسعه داده شده و در QCon در مورد آن صحبت شد؛ استفاده کردند. این مدل روشی مفید برای تفکر در مورد استفاده از این تکنولوژی ها است؛ بنابراین به ذهنم رسید که من هم توضیحات خودم را در مورد آن ارائه بدم. (پروتوکل های آورده شدهدر اینجا فقط برای مثال هستند؛ من فکر نمی کنم که این مثال ها ارزش کد نویسی و تست داشته باشند؛ و به همین دلیل ممکن است در جزئیات برخی مشکلات وجود داشته باشد.)

 

—————————————————————————————————–

سطح صفر

نقطه ی شروع برای مدل استفاده از HTTP به عنوان سیستم انتقال در تعاملات راه دور(remote) است، اما بدون استفاده مکانیزم های وب. در حقیقت چیزی که در اینجا انجام می دهید استفاده از HTTP به عنوان ساز و کار تونلینگ برای مکانیزم های تعاملات و فراخوانی های از راه‌دوری که دارید می‌باشد،به طور متداول بر اساس  Remote Procedure Invocation.

Figure 2: An example interaction at Level 0

 

فرض کنید می خواهم نوبتی را از دکترم بگیرم. نرم افزار نوبت دهی من در ابتدا نیاز دارد بداند دکتر من در یک تاریخ مشخص چه نوبت‌های آزادی دارد؛ به همین دلیل نرم افزار درخواستی را برای سیستم نوبت دهی بیمارستان جهت گرفتن این اطلاعات ایجاد می کند. در سناریو ی سطح صفر؛ بیمارستان یک service endpoint را با تعدادی آدرس URL ارائه می دهد. بعد از آن من به endpoint مذکور سندی که شامل جزیات درخواستم هست ارسال می‌کنم.

POST /appointmentService HTTP/1.1
[various other headers]

<openSlotRequest date = "2010-01-04" doctor = "mjones"/>

سرور سپس سندی که شامل اطلاعات درخواست شده می باشد را در جواب بر می گرداند:

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot start = "1400" end = "1450">
    <doctor id = "mjones"/>
  </slot>
  <slot start = "1600" end = "1650">
    <doctor id = "mjones"/>
  </slot>
</openSlotList>

من در اینجا از xml به عنوان مثال استفاده کردم؛ اما داکیونت می تواند به هر فرمت دیگری باشد؛JSON؛ YAML؛ Key-values pairs یا هر فرمت شخصی دیگر.

گام بعدی من رزرو نوبت است؛ که مجددا با post کردن یک سند به endpoint اینکار رو انجام میدم.

POST /appointmentService HTTP/1.1 [various other headers] <appointmentRequest> <slot doctor = “mjones” start = “1400” end = “1450”/> <patient id = “jsmith”/> </appointmentRequest> اگه

در صورتی که همه چی خوب پیش برود؛ جواب دریافت موفق آمیز نوبت را دریافت می کنم.

HTTP/1.1 200 OK [various headers] <appointment> <slot doctor = “mjones” start = “1400” end = “1450”/> <patient id = “jsmith”/> </appointment>
در صورت بروز مشکل، به عنوان مثال شخص دیگری قبل از من نوبت رو گرفته باشد، در بخش بدنه پاسخ یک سری پیغام خطا نیز دریافت می کنم

HTTP/1.1 200 OK
[various headers]

<appointmentRequestFailure>
  <slot doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
  <reason>Slot not available</reason>
</appointmentRequestFailure>

تا به اینجا این یک سیستم RPC ساده است. این مدل بسادگی ارسال و دریافت plain old XML(POX) است.  در صورتی که شما از SOAP یا XML-RPC نیز استفاده کنید، اساسا دارای رویکردی مشابه هستند، تنها تفاوت در این است که این رویکردها پیام های xml را در یکسری پوشش(wrapper) می پیچانند.

——————————————————————————————————————

سطح یک – منابع(Resources)

اولین گام در مسیر محبوبیت REST در RMM معرفی منابع است. در نتیجه در این سطح بچای ارسال تمام درخواست ها به یک endpoint مشخص، شروع به صحبت با منابع متمایز می کنیم.

Figure 3: Level 1 adds resources

ممکن است با کوئری اولیه‌مان، یک منبع برای دکتر خاص داشته باشیم.

POST /doctors/mjones HTTP/1.1
[various other headers]

<openSlotRequest date = "2010-01-04"/>

پاسخ اطلاعات اولیه مشابهی با پاسخ همین کوئری در سطح صفر برمی‌گرداند، اما هر نوبت اکنون یک منبع است که بصورت منحصر بفردی مشخص می شود.

HTTP/1.1 200 OK
[various headers]


<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

با منابع مشخص رزرو کردن یک نوبت به معنی ارسال درخواست post به نوبت مشخصی است.

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

اگر همه چیز به خوبی پیش برود مثل قبل پاسخ مشابهی را دریافت می کنم

HTTP/1.1 200 OK
[various headers]

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointment>

تفاوت اکنون این است که هرکسی در صورتی که بخواهد کاری را درباره ی نوبت دهی انجام دهد، مثلا رزور برخی آزمایش‌ها، ابتدا منبع نوبت را که معمولا دارای یک URL شبیه  http://royalhope.nhs.uk/slots/1234/appointment می باشد را گرفته، و به اون منبع درخواست ها خود را post می کنند.

برای کسی مثل من این شبیه مفهوم object identity می باشد. عوض صدا زدن چندین تابع در فضا و ارسال آرگومانها، ما متدی از یک آبجکت مشخص صدا میزنیم که آرگومانها را برای اطلاعات دیگر آماده می کند.

———————————————————————————————————–

سطح دوم – HTTP Verb ها

من  برای تمامی تعاملات در سطح یک و دو از ورب HTTP POST استفاده کردم، اما برخی افراد بجای آن یا علاوه بر آن از GET استفاده می کنند. در این دو سطح این مورد تفاوت چندانی ایجاد نمی کند؛ هر دو این ورب ها به عنوان مکانیزم های تونلینگ که به شما اجازه می دهند که تعامل خود از طریق HTTP رو تونل بزنید؛ استفاده می شوند. سطح دو حرکتی به این سمت است؛ استفاده از ورب های HTTP دقیقا شبیه به روشی که آنها توسط خود HTTP مورد استفاده قرار می‌گیرند.

Figure 4: Level 2 addes HTTP verbs

در مورد لیست نوبت هایمان، این بدین معنی است که می خواهیم از GET استفاده کنیم.

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

پاسخ شبیه به حالتی است است که درخواست بصورت POST بود.

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

در سطح دوم، استفاده از GET برای درخواست‌هایی مشابه این؛ بسیار مهم است. HTTP ؛GET را به عنوان یک عملیات امن(safe operation) تعریف می کند، که بدین معنی است که این درخواست تغییرات اساسی و بزرگی بر روی حالت هیچ چیزی اعمال نمی کند. این ویژگی به ما اجازه می‌دهد که درخواست های امن GET را به هر تعداد و به هر ترتیبی صدا بزنیم و هر بار نتیجه ی مشابهی را دریافت کنیم. یک نتیجه ی مهم در اینجا این است که به مشارکت کننده های در مسیریابی درخواست ها این امکان کش کردن  که یک عنصر اساسی در ساخت وب است را می‌دهد. HTTP شامل سنجش های متفاوتی جهت پشتیبانی از کش کردن است؛ که می تواند توسط تمام مشارکت کننده های در ارتباط؛ استفاده بشود. با دنبال کردن نقش های HTTP می توانیم مزیت های این توانمندی را بدست بیاوریم.

جهت رزرو یک نوبت نیاز به یک HTTP verb که حالت را عوض می کند داریم, یک POST یا PUT. من از POST مشابه که قبلا انجام دادم استفاده می کنم.

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

مصامحه میان استفاده از POST و PUT بسیار بیشتر از آن چیزی است که بخواهم در اینجا با جزییات به آن وارد شوم؛ ممکن یک روز در مورد آن مقاله ای جدا گانه‌ای را بنویسم. اما می خواهم این نکته رو یادآوری کنم که برخی افراد اشتباها یک تناظر میان PUT/POST و crate/update ایجاد می کنند. انتخاب میان انها کاملا متفاوت تر از آن است.

حتی اگر من از POST مشابهی که در سطح یک بود استفاده کنم؛ یک تفاوت اساسی دیگر در پاسخ سرویس ریموت وجود دارد. اگه همه چیز به خوبی پیش برود، سرویس پاسخی با کد پاسخ 201 بر می گرداند که مشخص می کنه که یک منبع جدیدی در دنیا-world- وجود دارد.

HTTP/1.1 201 Created
Location: slots/1234/appointment
[various headers]

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointment>

پاسخ 201 شامل یک ویژگی location بهمراه یک URL است که کلاینت می تواند از آن جهت GET کردن حالت جدید منبع در آینده استفاده کند. پاسخ در اینجا همچنین شامل نمایشی از آن منبع است تا کلاینت مجبور به یک فراخوانی اضافی نباشد.

یک تفاوت دیگر وجود دارد؛ در صورتی که موردی به اشتباه و خطا بر بخورد؛ از جمله اینکه کس دیگری نوبت را قبلا رزرو کرده است.

HTTP/1.1 409 Conflict
[various headers]

<openSlotList>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

بخش مهم این پاسخ استفاده از کد های پاسخ HTTP جهت نشان دادن اینکه خطا یا اشتباه رخ داده است می باشد. در این مورد یک 409 به نظر می رسد که انتخاب مناسبی برای مشخص کردن اینکه کس دیگری قبلا منبع رو به روش نامناسبی بروز رسانی کرده است؛ می باشد. بجای ارسال کد 200 البته بهمراه یک پاسخ خطا، در سطح دو ما صریحا از برخی از انواع پاسخ های خطا شبیه این استفاده می کنیم. این به طراح پروتوکل بر می گردد که چه کدهایی را استفاده کند؛ اما در صورتی که خطایی رخ بدهد بهتر است که پاسخ غیر 2xx استفاده شود. سطح دو استفاده از ورب ها و کدهای پاسخ HTTP را معرفی می کند.

کمی تناقض تملق گفتن وجود دارد. REST صحبت درباره ی استفاده از تمام HTTP verb ها را طرفداری می کند. آنها ممکن است با گفتن اینکه در حال یادگیری از موفقیت های عملی وب هستند این رویکرد را توجیه کنند. با این وجود world-wide web در عمل آنقدرها از PUT یا DELETE استفاده نمی کند. دلایل توجیه کننده ای برای استفاده بیشتر از PUT و DELETE وجود دارد؛ اما دلیل و گواه وجود وب از جمله این دلایل نمی‌باشد.

عناصر اصلی که توسط وجود وب پشتیبانی می شوند؛ تفکیک کامل میان عملیات امن(از جمله GET) و غیر امن؛ بهمراه استفاده از کدهای حالت جهت کمک به مطلع شدن از خطاهایی که با آن مواجه هستید می باشد.

—————————————————————————————————–

سطح سوم: Hypermedia Controls

سطح پایانی چیزی را معرفی می‌کند که احتمالا شما به عنوان یک سرواژه بد قواره به نام HATEOAS شنیده اید(Hypertext A The Engine Of Application State). این مورد پاسخی است به این سوال که به چه صورت می توان در میان لیستی از نوبت ها بدانیم جهت گرفتن یک نوبت چه کاری باید انجام داد.

Figure 5: Level 3 adds hypermedia controls

 

ما با GET اولیه ی مشابهی که در سطح دوم ارسال کردیم، شروع می کنیم.

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

 

اما پاسخ دارای عنصر جدیدی است.

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
     <link rel = "/linkrels/slot/book" 
           uri = "/slots/1234"/>
  </slot>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
     <link rel = "/linkrels/slot/book" 
           uri = "/slots/5678"/>
  </slot>
</openSlotList>

حالا هر نوبت دارای یک عنصر لینک شامل URL ای است که به ما اعلام می کند که به چه صورت می توان یک نوبت را رزرو کرد.

نکته ی hypermedia control‌ها این است در گام بعدی چه حرکتی می توانیم انجام داد؛ و URL مرتبط با منبع که قصد دستکاری کردن آن را داریم را به ما می‌گوید. بجای اینکه بخاطر سپردن آدرسی که باید درخواست post خود را جهت گرفتن نوبت ارسال کنیم؛ hypermedia controlها در پاسخ چگونگی انجام این کار را به ما می گویند.

 

در اینجا نیز POST کپی ای از چیزی است که در سطح دوم انجام گرفت.

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

و پاسخ شامل تعدادی از hypermedia controlها برای موارد مختلفی که در ادامه می توان انجام داد می باشد.

HTTP/1.1 201 Created
Location: http://royalhope.nhs.uk/slots/1234/appointment
[various headers]

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
  <link rel = "/linkrels/appointment/cancel"
        uri = "/slots/1234/appointment"/>
  <link rel = "/linkrels/appointment/addTest"
        uri = "/slots/1234/appointment/tests"/>
  <link rel = "self"
        uri = "/slots/1234/appointment"/>
  <link rel = "/linkrels/appointment/changeTime"
        uri = "/doctors/mjones/slots?date=20100104@status=open"/>
  <link rel = "/linkrels/appointment/updateContactInfo"
        uri = "/patients/jsmith/contactInfo"/>
  <link rel = "/linkrels/help"
        uri = "/help/appointment"/>
</appointment>

یکی از مزیت های مشهود و مشخص hypermedia control این است که به سرور اجازه می دهد که شمای URI خود رو بدون اینکه کلاینت ها نیاز به تغییر داشته باشند تغییر دهد. مادامی که کلاینت ها به URI لینک “addTest” نگاه می کنند؛ تیم سرور می توانند تمام URLها را بجز نقاط ورودی(entry point)؛ دستکاری کند.

مزیت دیگر این است که به توسعه دهنده های کلاینت اجازه می دهد که پروتوکل  را واکشی و بررسی کنند. لینک ها به توسعه دهنده های کلاینت در مورد گام‌های بعدی که ممکن است انجام دهند راهنمایی می دهند: هر دو کنترلر “self” و “cancel” به URI مشابهی اشاره دارند- اما توسعه‌دهنده‌ها نیاز دارند بدانند که یکی از آن عملیات GET و دیگری DELETE است. اما حداقل این نکته را به آنها می‌دهد که برای اطلاعات بیشتر در مورد چه چیزی فکر کنند و به دنبال URI مشابه در مستندات پروتوکل باشند.

بطور مشابه این مورد همچنین به تیم سرور اجازه می دهد که ویژگی‌های جدید را با قرار دادن لینک های جدید در پاسخ اطلاع رسانی کند. اگر توسعه دهنده های کلاینت توجه خود را بر روی لینک های جدید معطوف کنند این لینک ها می تواند برای شناسایی بیشتر یک تلنگر باشند.

استاندارد مستقلی جهت چگونگی نمایش hypermedia control ها وجود ندارد. چیزی که من اینجا انجام دادم استفاده از توصیه های جاری تیم “REST in Practice” می باشد, که از ATOM(RFC 4287) استفاده می کند، من یک عنصر <link> با یک ویژگی uri برای URI مقصد و یک ویژگی rel برای توصیف انواع ارتباط استفاده کردم. یک ارتباط شناخته شده(شبیه self جهت اشاره به خود عنصر) ساده است, هر ویژگی به آن سرور یک URI واجد شرایط است. ATOM بیان می کند که تعاریف برای linkler های شناخته شده در Registry of Link Relations موجود است. همانطور که نوشتم اینها به مواردی که توسط ATOM انجام شده است محدود شده است، و عموما به عنوان پیشروها در سطح سوم restfulness دیده می شود.

—————————————————————————————————-

معنی سطح ها

باید تاکید کنم که RMM؛ هر چند یک راه مناسب برای تفکر درباره ی عناصر REST می باشد؛ اما تعریفی از خود سطوح REST نیست. Roy Fielding این را بدین صورت روشن کرده است که سطح سوم RMM یک پیش شرط REST است. همانند بسیاری از اصطلاحات در نرم افزار، REST تعاریف بسیاری را بخود گرفته است، اما از آنجایی Roy Fielding این اصطلاح را ابداع کرد، تعریف او باید وزن بیشتری نسبت به سایرین داشته باشد.

چیزی که من در مورد RMM مفید می دانم این است که آن یک راه قدم به قدم مناسب جهت فهم و درک ایده های اولیه و اساسی ورای تفکر restful مهیا می کند. همانطور که RMM را به عنوان ابزاری جهت کمک به ما جهت یادگیری درباره ی مفاهیم و نه فقط چیزی که باید در برخی از مکانیزم های ارزیابی استفاده شود می بینم. اما فکر نمی کنم که مثال های کافی تا بحال داشته باشیم که نشان دهد که رویکرد restful راه صحیح برای یکپارچه کردن سیستم ها است، من فکر می کنم این یک رویکرد بسیار جذاب است و از جمله موردی است که اکثر مواقع آن را توصیه می کنم.

در صحبتی در این باره با Robinson , تاکید داشت هنگامی که برای اولین بار Leonard Richardson آنرا را ارائه داد، چیزی که برایش جذاب بود؛ ارتباط آن با تکنیک های طراحی متدوال بود.

  • سطح یک پرسش استفاده از تقسیم و غلبه جهت کنترل کردن پیچیدگی را مورد هدف قرار می‌دهد؛ با شکستن endpoint یک سرویس بزرگ به چندین منابع.
  • سطح دوم یک مجموعه استاندارد از verb ها را معرفی می‌کند که در نتیجه ما موقعیت‌های مشابه را را به روشی یکسان کنترل؛ و اختلافات غیر ضروری را حذف می کنیم.
  • سطح سوم قابلیت کشف کردن را مهیا کرد، که روشی را برای ایجاد یک پروتوکل با خاصیت خود مستندی بیشتر را مهیا کرد.

نتیجه مدلی است که به ما کمک می کند که درباره ی نوع سرویس HTTP که می خواهیم ارائه دهیم فکر کنیم و توقعات افرادی که میخواهند با آن تعامل داشته باشند را تنظیم کنیم.

درباره ی masoud@admin

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

به کانال من در تلگرام بپیوندید

اگر علاقمند به مباحث معماری و مدلسازی نرم افزار هستید می توانید به کانال تلگرام بپیوندید.

پیوستن بستن