namespace example.weather

/// Provides weather forecasts.
@paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize")
service Weather {
version: "2006-03-01"
resources: [
City
]
operations: [
GetCurrentTime
]
}

resource City {
identifiers: { cityId: CityId }
properties: { coordinates: CityCoordinates }
read: GetCity
list: ListCities
resources: [
Forecast
]
}

resource Forecast {
identifiers: { cityId: CityId }
properties: { chanceOfRain: Float }
read: GetForecast
}

//"pattern" is a trait.
@pattern("^[A-Za-z0-9 ]+$")
string CityId

@readonly
operation GetCity {
input := for City {
//"cityId" provides the identifier for the resource and
// has to be marked as required.
@required
$cityId
}

output := for City {
//"required" is used on output to indicate if the service
// will always provide a value for the member.
//"notProperty" indicates that top-level input member "name"
// is not bound to any resource property.
@required
@notProperty
name: String

@required
$coordinates
}

errors: [
NoSuchResource
]
}

// This structure is nested within GetCityOutput.
structure CityCoordinates {
@required
latitude: Float

@required
longitude: Float
}

//"error" is a trait that is used to specialize
// a structure as an error.
@error("client")
structure NoSuchResource {
@required
resourceType: String
}

// The paginated trait indicates that the operation may
// return truncated results.
@readonly
@paginated(items: "items")
operation ListCities {
input := {
nextToken: String
pageSize: Integer
}

output := {
nextToken: String

@required
items: CitySummaries
}
}

// CitySummaries is a list of CitySummary structures.
list CitySummaries {
member: CitySummary
}

// CitySummary contains a reference to a City.
@references([
{
resource: City
}
])
structure CitySummary {
@required
cityId: CityId

@required
name: String
}

@readonly
operation GetCurrentTime {
output := {
@required
time: Timestamp
}
}

@readonly
operation GetForecast {
input := for Forecast {
//"cityId" provides the only identifier for the resource since
// a Forecast doesn't have its own.
@required
$cityId
}

output := for Forecast {
$chanceOfRain
}
}

將其分成單獨(dú)的部分,我們可以在頂部看到代碼的控制部分:

$version: "2"

下面是實(shí)質(zhì)性代碼:

namespace example.weather

/// Provides weather forecasts.
@paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize")
service Weather {
version: "2006-03-01"
resources: [
City
]
operations: [
GetCurrentTime
]
}

resource City {
identifiers: { cityId: CityId }
properties: { coordinates: CityCoordinates }
read: GetCity
list: ListCities
resources: [
Forecast
]
}

resource Forecast {
identifiers: { cityId: CityId }
properties: { chanceOfRain: Float }
read: GetForecast
}

這里我們有服務(wù)的定義(及其分頁(yè))以及所利用的資源以及對(duì)這些資源采取的操作。

//"pattern" is a trait.
@pattern("^[A-Za-z0-9 ]+$")
string CityId

@readonly
operation GetCity {
input := for City {
//"cityId" provides the identifier for the resource and
// has to be marked as required.
@required
$cityId
}

output := for City {
//"required" is used on output to indicate if the service
// will always provide a value for the member.
//"notProperty" indicates that top-level input member "name"
// is not bound to any resource property.
@required
@notProperty
name: String

@required
$coordinates
}

errors: [
NoSuchResource
]
}

// This structure is nested within GetCityOutput.
structure CityCoordinates {
@required
latitude: Float

@required
longitude: Float
}

//"error" is a trait that is used to specialize
// a structure as an error.
@error("client")
structure NoSuchResource {
@required
resourceType: String
}

// The paginated trait indicates that the operation may
// return truncated results.
@readonly
@paginated(items: "items")
operation ListCities {
input := {
nextToken: String
pageSize: Integer
}

output := {
nextToken: String

@required
items: CitySummaries
}
}

// CitySummaries is a list of CitySummary structures.
list CitySummaries {
member: CitySummary
}

// CitySummary contains a reference to a City.
@references([
{
resource: City
}
])
structure CitySummary {
@required
cityId: CityId

@required
name: String
}

@readonly
operation GetCurrentTime {
output := {
@required
time: Timestamp
}
}

@readonly
operation GetForecast {
input := for Forecast {
//"cityId" provides the only identifier for the resource since
// a Forecast doesn't have its own.
@required
$cityId
}

output := for Forecast {
$chanceOfRain
}
}

從這里開(kāi)始,我們有一系列特征定義和嵌套結(jié)構(gòu),允許在模型內(nèi)排列輸出和功能,從而更好地控制基本輸出和功能。查看此代碼,您可以看到特征定義如何解鎖功能控制。本質(zhì)上,特征允許在不從根本上改變底層實(shí)體的情況下約束和格式化數(shù)據(jù)和服務(wù)。

Smithy的優(yōu)點(diǎn)和缺點(diǎn)

考慮到這一點(diǎn),使用像 Smithy 這樣的東西有什么優(yōu)點(diǎn)和缺點(diǎn)?

優(yōu)點(diǎn)

對(duì)于大多數(shù)用戶來(lái)說(shuō),采用 Smithy 的最大好處是它與協(xié)議無(wú)關(guān)。并非所有開(kāi)發(fā)都經(jīng)過(guò)提前考慮,您今天使用的協(xié)議永遠(yuǎn)都是正確的。采用像 Smithy 這樣的解決方案,您可以為當(dāng)前的工作創(chuàng)建模型,這些模型可以隨著新的開(kāi)發(fā)而變形、改變和改變。這為您提供了很大的靈活性,最終使您的開(kāi)發(fā)擺脫了嚴(yán)格的限制。

專注于模型驅(qū)動(dòng)系統(tǒng)可以實(shí)現(xiàn)機(jī)器和人類的高效可讀性。模型可以從一個(gè)系統(tǒng)移植到另一個(gè)系統(tǒng),并轉(zhuǎn)換為其他格式和類型,從而將底層系統(tǒng)從外部控制的負(fù)擔(dān)中解放出來(lái)。模型也很容易比較,可以實(shí)現(xiàn)更好的自動(dòng)化,從而提高效率。

Smithy 也是開(kāi)源的,這是一個(gè)巨大的好處!與協(xié)議無(wú)關(guān)的解決方案很棒,但是當(dāng)這種解決方案的開(kāi)發(fā)也與源代碼控制解耦時(shí),可能會(huì)使其關(guān)閉并集中控制,這會(huì)使其開(kāi)發(fā)和迭代速度更快。 Smithy 是一個(gè)可以獨(dú)立完成很多事情的工具,但從長(zhǎng)遠(yuǎn)來(lái)看,將其開(kāi)放給社區(qū)開(kāi)發(fā)和迭代可以使其成為一些非常令人驚奇的事情的平臺(tái)!

缺點(diǎn)

雖然 Smithy 在其基于模型的系統(tǒng)上做了很多出色的工作,但它提供代碼生成作為一個(gè)重要功能。事實(shí)上,代碼生成仍然存在重大問(wèn)題,即使這里提供了大量的修復(fù)和系統(tǒng),這些問(wèn)題也不會(huì)消失。使用 Smithy 進(jìn)行代碼生成通常很有效,但與任何代碼生成工具一樣,99% 的情況下,輸出都可以,但這 1% 意味著您需要非常密切地關(guān)注并投入資源進(jìn)行審查,這削弱了從代碼生成中獲得的一些好處。

Smithy 功能齊全。在某些情況下,這可能是負(fù)面的。如果您希望構(gòu)建一些非常簡(jiǎn)單的東西,只有少數(shù)人與之交互,那么為其他觀看者進(jìn)行投影或?yàn)槎鄠€(gè)團(tuán)隊(duì)創(chuàng)建模型訪問(wèn)規(guī)則的想法可能不適合您。在這種情況下,讓史密斯加入的成本可能不合理。與任何事情一樣,您應(yīng)該考慮 Smithy 是您需要使用的東西還是您只想使用的東西。

最后,需要簡(jiǎn)單說(shuō)明的是,這是亞馬遜開(kāi)發(fā)的產(chǎn)品。雖然這當(dāng)然不應(yīng)該是采用或不采用工具的唯一原因,但該項(xiàng)目的長(zhǎng)期維護(hù)和健康與亞馬遜本身的長(zhǎng)期維護(hù)和健康息息相關(guān)。雖然亞馬遜看起來(lái)不會(huì)很快走向任何地方,但對(duì)于那些在網(wǎng)絡(luò)開(kāi)發(fā)領(lǐng)域工作了很長(zhǎng)一段時(shí)間的人來(lái)說(shuō),在其首次發(fā)布十年或二十年之后,企業(yè)發(fā)展陷入困境和崩潰的故事比比皆是,而這個(gè)肯定應(yīng)該是一個(gè)考慮因素。即使產(chǎn)品是開(kāi)源的,開(kāi)源并不意味著永久開(kāi)發(fā),并且在任何特定時(shí)刻都可能會(huì)認(rèn)為 Smithy 不值得花費(fèi)時(shí)間或資源。

開(kāi)源軟件也有轉(zhuǎn)變?yōu)楦嗌虡I(yè)許可證的趨勢(shì)。從版本 2.15 開(kāi)始,Buoyant Enterprise for Linkerd 宣布 Linkerd 將不再生成開(kāi)源穩(wěn)定版本。截至 2024 年,Redis 有爭(zhēng)議地放棄了 BSD 許可證,引起社區(qū)許多人的嚴(yán)重?fù)?dān)憂。不幸的是,這些故事很常見(jiàn),并說(shuō)明了像 Smithy 這樣的解決方案的一個(gè)巨大問(wèn)題——它適合開(kāi)源框架,直到它不適合,而這種轉(zhuǎn)變可能會(huì)產(chǎn)生巨大的影響。

 結(jié)論

最終,對(duì)于采用強(qiáng)大的基于模型的解決方案的系統(tǒng)來(lái)說(shuō),Smithy 是一個(gè)不錯(cuò)的選擇。雖然它對(duì)于所有實(shí)現(xiàn)來(lái)說(shuō)可能都太重了,但它的靈活性和可擴(kuò)展性使其成為許多情況下可靠的價(jià)值主張。

原文鏈接:https://nordicapis.com/overview-of-smithy-an-api-description-language-from-amazon/

上一篇:

掌握合約優(yōu)先API開(kāi)發(fā):關(guān)鍵策略和優(yōu)勢(shì)

下一篇:

.NET Core Web API + Vue By Linux and Windows 部署方案知識(shí)點(diǎn)總結(jié)
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門(mén)場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)