ááá áááá á¨áśááľáá áááśá˝ áááľ ááťáá á ááŤáł á áááááśá˝á áŤáá áá¸áᢠáĽá ááᣠá¨á áááááľ áťááá˝ á¨á¨áĽá áá ááá˝ á¨á ááťá¸á á˝ááŽá˝ ááá ááááᢠá¨áá ááááą á˝áá áá°á áá áááľá á¨á ááŤáł á¨á˘áá°á á áááááľ áĽáŤááá˝á áá° á ááľ áĽá á áá¸á á˛áá áá á áŁáşáá ááŁááá˘
áŁá˝ ááááŁá áá á¨á°á áá á á ááťá¸á ááá á áŽáľ ááá˝ááľ á áá¤áśáš á°áľá°á áááá áá˝ááᢠáá
áá´ áĽááľá áĽáá°ááŤáľáĄáľ á á áŞá áá ááá á áá°áá. áá°ááŤáŠ ááááá˝ áĽá á á°ááŤáŠ áááłáá˝ ááľáĽ, áááľááá˝ á áŁá áááŤáŠ áá˝áá. á¨á°áá°á ááłááá˝á á áá áá á¨á ááŤáł á ááŤá¨áŚá˝á áĽá
áá˝ áĽá ááłáśá˝ á áłááťááá˘
á¨ááłáŤ ááŽáááľ
ááá˝ áááľá¨áᣠá áá áĽá¨á áŤáá áľ áŁáá áá°áá áŞáŤ ááľáĽ áŤááľ á áááááśá˝ ááľáĽ á ááąá ááłá áĽáááá¨áľá˘
áá áĽááľ á¨ááľá¨á áá፠ááĽáŤáŞáŤá¨ááĽá á ááťá¸á á˝áá á áŁá á á ááá áá áĽá á¨áľáááá ááááá˝ ááá ááľá¨áŽá˝ á áááłáá. áá
á˝áá á˝ááŽá˝á áĽá áááľááá˝á áááłá¨áľ á¨áá°á + áŽáľáá áŽáľ ááłááá˝á áá áááᢠáŽáľáá áá፠áĽá á C # ááá˘áá˝ áĽáŠá ááá¨áłáľ (ááá ááá¨áłáľ á¨áááťá) áá ᣠá á°á¨ááŞá ᣠáŽáą á¨á፠á¨á áá á¨áłáá áĽá ááá¨áłáľ á¨ááťá ááᢠááášá
á¨á፠ááá˘áá˝ ááá¨áłáľ ááá áááľá¨á, ᨠKotlin áĽáá ââá áľááľá á áľáááłáá áĽá áá á áľááľá (á áááŚá ááááľ) áĽáť áĽá áááá. áĽááľ á¨á¤ááľá´áá˝á áá´áá˝ áááŤá ᣠáá á áĽáááą áááá á¨á፠ááŽááŤááŽá˝ áĽáá° áá áá´áá˝ áŤááá ᣠáľááá
áá
á¨ááľááá áŁáá á¨ááŤá áá¸á áľáá˝ áľáłá ááááá˘
á¨á°ááľ áá¨áá፠á áááááľ á á. á ááľ á°á á°ááľ á ááá
áś áááááľ áŤáááŁáᣠá áá
áá á ááľááśá˝ áá°á¨áá áĽá á áá¨á¨áťá á°ááą áľááááľ áá áá°ááłáᢠá¨áá˝á°á á áááááľ áŤáą áľá á°ááśá˝ ááá á áŤáá
áᥠáĽáá
áĽá á¨ááááĽá¨á áľáá˝ á°á¨á᪠á°ááŁáŤáľ áŤááľ á¨á á˝áłááá˝ ááááľ áĽáť ááá˘
áľááá , á áĽáŤááłááłá¸á ááľáĽ á áľááľá á¨á°áá°á á¨á°áłáłááá˝ áľáĽáľáĽ áŤáá¸á áťáľ áŠá (á¨á°ááśá˝ áá á¨ááááľ) á á. áĽáá° áá°á á ááááśá˝áŁ áááĽááśá˝ á˝áá áĽá áááá˝á áááá áĽá ááážá˝ ááá ááľá°ááá áá˝áááĄ-
data class ChatMessage(
// nullable ŃĐ°Đş как пОŃвНŃĐľŃŃŃ ŃОНŃкО пОŃНо persist
val id: Long? = null,
/** ĐĄŃŃНка на авŃĐžŃĐ° */
val author: UserReference,
/** ХООйŃонио */
val message: String,
/** ĐĄŃŃНки на Đ°ŃŃĐ°Ńи */
// иС-Са ĐžŃОйоннОŃŃоК ŃвŃСки JPA+ĐĄĐŁĐĐ ĐżŃĐžŃĐľ пОддоŃМиваŃŃ Đ¸ null, и ĐżŃŃŃŃĐľ ŃпиŃки
val files: List<FileReference>? = null,
/** ĐŃНи ŃвНŃĐľŃŃŃ ĐžŃвоŃОП, ŃĐž СдоŃŃ ĐąŃĐ´ĐľŃ ĐžŃигинаН */
val replyTo: ChatMessage? = null,
/** ĐŃНи ŃвНŃĐľŃŃŃ ĐżĐľŃĐľŃŃНкОК, ŃĐž СдоŃŃ ĐąŃĐ´ĐľŃ ĐžŃигинаН */
val forwardFrom: ChatMessage? = null
)
ááá áĽá á¨á°á áá á áááá˝ áá° ááá˝ ááŤáá˝ á áááá˝ áá¸áᢠáĽáá á¨áááá¨á áĽáá°áá áááĄ-
typealias FileReference = Long
typealias UserReference = Long
á¨á°á áá ááἠá ááá ááĽáľ ááľáĽ á°á¨áá˝áś á REST á áŠá á°áááˇáᢠááááá˝á á°ááłáłá ááᥠáááá˝ áĽá ááłá˘ááááá˝á áľááĽááą á á°áᨠá¨ááá áá¨ááť á áááááľ ááľáĽ áááŤáá˘
áá° áĽááá á áááááśá˝ á¨áá°á¨á áĽáŞáá˝ á áá áá¸áᢠá¨áŁáľ áĽáŤááá˝. áá áááľ áĽááá á áĽáŤááá˝ á¨áááá áá፠á áśáľá°á ááá á áááááľ áĽáá˛áľá°áááą á¨áááá áá á¨á áá áá áááľ ááᢠá áĽá á¨áá¨áŤ ááá áŽá˝ ááᣠááĽáá°áá á áááľ á áááááśá˝ á¨á°ááá°á á¨áĽáŞ áá 100 áá° ááᣠáľááá áĽááá á ááĽáŽá˝ áá°ááľ áĽáá ááááá˘
á¨áá¨á¨áťáá ᨠN áááááśá˝á á¨ááá á áľááá áá¨ááá˝ áááá á ááá ᨠREST áááŁá áŞáŤ ááľáŤáľ á ááĽáᢠáááľáᣠá áááŁáŠ ááľáĽ áŤáá á¨áááĽááľ áá´á á¨áá áá°á á°ááłáłá áá áĽáá ááá áá¨ááá˝ ááá á ááŁá¸á áĽáá áĽáááááᢠá ááľ-áá¨á¨áť áá´á ááŤá¨á áŤáá ááŠááľ ááá áĽá á°á ááá á áááá˝á áááľá¨á á áľááš á˛ááŞááľ á áá ááአáá á¨áĽ á ááŁá¸ááĄ-
/** Đ ŃакОП видо ĐžŃĐ´Đ°ŃŃŃŃ ŃŃŃНки на ŃŃŃнОŃŃи Đ´ĐťŃ ŃŃОнŃĐ° */
data class ReferenceUI(
/** ĐдонŃиŃикаŃĐžŃ Đ´ĐťŃ url */
val ref: String,
/** ĐидиПОо пОНŃСОваŃĐľĐťŃ Đ˝Đ°ĐˇĐ˛Đ°Đ˝Đ¸Đľ ŃŃŃНки */
val name: String
)
data class ChatMessageUI(
val id: Long,
/** ĐĄŃŃНка на авŃĐžŃĐ° */
val author: ReferenceUI,
/** ХООйŃонио */
val message: String,
/** ĐĄŃŃНки на Đ°ŃŃĐ°Ńи */
val files: List<ReferenceUI>,
/** ĐŃНи ŃвНŃŃŃŃ ĐžŃвоŃОП, ŃĐž СдоŃŃ ĐąŃĐ´ĐľŃ ĐžŃигинаН */
val replyTo: ChatMessageUI? = null,
/** ĐŃНи ŃвНŃŃŃŃ ĐżĐľŃĐľŃŃНкОК, ŃĐž СдоŃŃ ĐąŃĐ´ĐľŃ ĐžŃигинаН */
val forwardFrom: ChatMessageUI? = null
)
á¨áá¨á°ááľá áá°áá á á ááĽáá˘
interface ChatRestApi {
fun getLast(n: Int): List<ChatMessageUI>
}
á¨áŠá á ááľáľáááľ áááľ ááááŁáŠ á¨DTO áá´áá˝ áááľ ááᣠáááľáᣠá REST á áŠá ááááá áŤááĽáá˘
áĽáá áá á¨ááŤáľá°ááá ááá ááá á áááľ á¨ááááľ ááłáá፠áĽáŤáłááá á ááááá˝á áĽá á¨ChatMessage/ChatMessageUI áá´á áĽááłá á¨ááá ááá ááᢠáá áá áŤá°á¨áŠáľ á¨ááłááášá áŽáľ áááá¨ááá áá áĽáŹ áá (áťáśáš á¨á°ááá áá¸áᣠáľááá á ááľ áĽáť áĽááłáá ááááľ áĽáá˝ááá)á˘
áááľááá áľáá¨ááááąá á¨ChatMessageUI ááá áĽá ᨠChatRestApi.getLast áá´ á¨áááá áłáł á áááľá á¨áá áááľ á áĽáááą á¨áłáá á ááá
ááᢠáá
á JDK ááľáĽ ááĽá ááᣠáľááá
á á ááááš á°á¨á á¨ááĽá¨ áááŽá˝á á
á°á á°á¨á°á ááá
(á˛á¨áአáĽá á˛áááą áľáááá áá á á
) á áá°áŤáᢠáľááá
á¨áłáá áľáĽáľáĽ á ááŤáľáááá áľ áá ááááá áá áá á¨á°ááá° á°ááŁá ááá (LinkedHashSetá á á ááá áá áá
á áááá˝ á áá°áá)á˘
á áľááá áá°áĽáĄ á¨á
á á¨ááážá˝ ááá á¨ááľá°ááá á°áá°ááśá˝ áĽáá°áá áĽááááłááᢠáŤá áááľ áĽááą á á, áá ááááłá¸á á¨áśáľáľ áááĽááśá˝ á áá ááĽá. ááá á¨áááĽááľ á°áá°ááľ áá° áááŁá áá°ááá á áá áľá˘
á¨ááŤá á áááááśá˝ ááἠáááá á á¨áá¨á°ááľ á¤áá ááá˝ á ááĄ
interface ChatMessageRepository {
fun findLast(n: Int): List<ChatMessage>
}
data class FileHeadRemote(
val id: FileReference,
val name: String
)
interface FileRemoteApi {
fun getHeadById(id: FileReference): FileHeadRemote
fun getHeadsByIds(id: Set<FileReference>): Set<FileHeadRemote>
fun getHeadsByIds(id: List<FileReference>): List<FileHeadRemote>
fun getHeadsByChat(): List<FileHeadRemote>
}
data class UserRemote(
val id: UserReference,
val name: String
)
interface UserRemoteApi {
fun getUserById(id: UserReference): UserRemote
fun getUsersByIds(id: Set<UserReference>): Set<UserRemote>
fun getUsersByIds(id: List<UserReference>): List<UserRemote>
}
ááŤá á áááááśá˝ ááááŞáŤ áá ááĄáľá áá°áľ áĽáá°áá°áĄ áĽá á áááąá áľáŞáśá˝: á á´áľ (á¨á¤ááááśá˝á á á°á á°á¨á°á áłáá áĽá á áአáááá˝) áĽá á List (áĽááśá˝ ááአáá˝áá - áľááá á°á áĽá áááŤá) ááłá áá˝ááá˘
ááá á á°ááŁá á
á¨áá á á°ááŁá á
á¨REST áááŁá áŞáŤá˝á á¨ááááŞáŤá ááá áľáá ፠á á áĽááááš ááłáŽá˝ áĽáá°áá áŤá ááá áááľááá˘
class ChatRestController(
private val messageRepository: ChatMessageRepository,
private val userRepository: UserRemoteApi,
private val fileRepository: FileRemoteApi
) : ChatRestApi {
override fun getLast(n: Int) =
messageRepository.findLast(n)
.map { it.toFrontModel() }
private fun ChatMessage.toFrontModel(): ChatMessageUI =
ChatMessageUI(
id = id ?: throw IllegalStateException("$this must be persisted"),
author = userRepository.getUserById(author).toFrontReference(),
message = message,
files = files?.let { files ->
fileRepository.getHeadsByIds(files)
.map { it.toFrontReference() }
} ?: listOf(),
forwardFrom = forwardFrom?.toFrontModel(),
replyTo = replyTo?.toFrontModel()
)
}
ááá ááá á áŁá ááá˝ áá, áĽá áá
áľáá
á°á¨á᪠áá.
áŁá˝ ááááŁá áá áĽáá áááá áĽá áá¨áá á¨ááŤá á áááááľ á áĄáľá áĽááá áááᢠáá áááłáááłá˝á áá áááá?
ááĽáŤááłááą áááĽááľ á á°áŤá˛á ááľá áá áá¨á áááááľ á ááľ áĽáŞ áá° UserRemoteApi áĽá áááá á¨á°áŤáŤá áááá˝á áááááľ áá° FileRemoteApi á ááľ áĽáŞ áá°á¨ááᢠ፠áááľááᢠáChatMessage ᨠforwardFrom áĽá replyTo ááľáŽá˝ á¨á°áááľ áá á ááľááá áĽáŞáá˝á á áááááá áľ ááááľ áá áĽáá áᢠááá áá áĽááąá áá° ChatMessageUI ááá¨á áá° áľáááá˝ áááŤáᣠáááľá á¨áĽáŞ ááŁáŞáá˝ á á¨áá°á áááł áá¨áአáá˝ááᢠáá°á áĽáá áĽáá°á ááľáá, áĽá áááá˝ áĽáá°ááá áĽá á°áá°ááą á áśáľáľ áááĽááśá˝ á¨á°áá°á áĽáá°áá áĽááľáĽ.
á áá¤áąá, á á ááľ áááĽááľ ááľáĽ á¨áááľ áĽáľá¨ áľáľáľáľ áĽáŞáá˝ áá° ááŤá á áááááśá˝ áĽá áá á ááá á¨áááĽááľ ááŹá á ááľ á¨áá áĽáŞ áĽááááá. á á ááá á¨áĽáŞáá˝ áĽááľ á¨2*N+1 áĽáľá¨ 6*N+1 áááŤáŤáᢠáá á áĽááá°á áááá˝ ááľáĽ áá áŤá á áá? á ááľ áá˝ áááľáŤáľ 20 áááááśá˝á áááľáłá áĽáá áᢠáĽááąá áááá á ᨠ4 áĽáľá¨ 10 á°á¨ááľ áááľáłá. á áľááŞ! á 500 ms ááľáĽ ááľááἠáĽááááá. áĽá á áá°áá ááá ááľáĽ áĽáá¨á á¨áá˝ áá¸áĽáá áááľá¨á áľáá°áĄ ááá á¨áá¨á¨áť ááĽáĽ á¨á áááá ááľáááśá˝ á áĽáĽá áá¨áአáá˝ááá˘
áááśá˝
- áŽáą áĽáĽá ááĽá áŤá áĽá áĽáŤáąá á¨áťá á°ááľ áá (á¨áľáá áĄáľá á áá)á˘
- áŽáą ááá áá, áľááá áĽáŤáľáá á áĽáá ááá°áŽáľ ááá áĽáľáá˝ á¨áá áááľ ááťáá.
- áŁá˝ ááááŁá á áŁááľ ááá á áááľáá áĽá áŚáááá á áá ááአáá° á áááᎠá¨á°ááá° ááá˘
- á¨ááá áááŚá˝ á ááá á¨áá°á¨á áĽá á áŤáŁá˘áŤá ááááá˘
ááááľ
á áŁá áľáá˝ áĽá˝áá˝ ááááŤáľ á áľá᪠á ááťá¸á.
áá á áŤááľ áĽá áá á ááá á áááááśá˝ ááá á ááŽáśáłáá ááľáĽ ááłá áá˝ááᢠáááŚá˝á á¨ááľá¨á ááĽááľ á áľááá á¨áá, áľáááąá áááłá°áĽ áá á¨ááá. á á°ááłáłá áá, áááá á áááááłá˝á á áááá á áŁá á áľá᪠áá, áľááá á¨áá á á°áŤá á°ááŁáŤáááľ áá°á á áŁá á áŁáĽ áá.
ááá áľáአáá°áľ
áááá áááááśá˝ á áľáአááľáŹáľ áááá áá˝áá - áá á áááĽááśá˝ áĽááľ áá á áááľá¨áľ á áá ááľáĽ áŤááá á¨ááľáá áááŞá áááľáááľ áŤáľá˝áááłáᢠáá á á°áá áĽáŠ ááááľ á áá°áá ááááŤáąá á ááŤá á áááááľ áá áľáá á¨áá°á áááľ áŤáľá¨áľáá.
áľáአáá°áľá áá°áá á á áŁá ááá áááĄ-
override fun getLast(n: Int) =
messageRepository.findLast(n).parallelStream()
.map { it.toFrontModel() }
.collect(toList())
áľáአá¨áááĽááľ áááá áŞáŤá á áá áá á¨300-700 áá´á á ááłáĽ á°á¨á áĽáááááᣠáá á á¨áá ááľ áľáá ፠á áŁá á¨á°áťá ááᣠááá áá á ááá á á ááŁá á áá°ááá˘
á áá á ááŤá¨áĽáŁ á¨á°á áá áá¨ááť áĽá á¨ááá áá¨ááť áĽáŤááá˝ á á°ááłáłá ááአááá¸ááᣠáá á°áá á áŁá áááŁá á áá°ááᢠáá áá áááľá°áŤá¨á á¨áĽáŞ á ááááŽáá á áŁá ááá¨á á ááĽááľá˘ áááłáᣠá áá ááá፠á°á¨á (CompletableFuture á°áĽá á¨áá áŤ)áĄ-
private fun ChatMessage.toFrontModel(): ChatMessageUI =
CompletableFuture.supplyAsync {
userRepository.getUserById(author).toFrontReference()
}.thenCombine(
files?.let {
CompletableFuture.supplyAsync {
fileRepository.getHeadsByIds(files).map { it.toFrontReference() }
}
} ?: CompletableFuture.completedFuture(listOf())
) { author, files ->
ChatMessageUI(
id = id ?: throw IllegalStateException("$this must be persisted"),
author = author,
message = message,
files = files,
forwardFrom = forwardFrom?.toFrontModel(),
replyTo = replyTo?.toFrontModel()
)
}.get()!!
ááááŞáŤ áá ááá á¨áŤááł áŽáľ ááá¨áłáľ ááá áĽá¨áá ááĽáˇá. áá á¨ááá áľ ááááŤáľ áá° ááŤá á áááááśá˝ á¨áá°á¨á áĽáŞáá˝á áá¤áśáš áĽá á áá á¨ááá áľ ááá¨áľ áá á¨áĽáᢠáá á áŤáą ááĽá á áá°áá. áá áĽáŞáá˝á ááŁáá á áŁá á¨ááŤáá á áááľáá áĽá á¨á°ááá°á ááá˝ á°áŞ âááľáâ áá áááłá°ááá˘
áŽáá˛áá˝á á¨á°á áá ᣠááá ááá á¨á áá á¨á áááľáá
private fun ChatMessage.toFrontModel(): ChatMessageUI =
join(
{ userRepository.getUserById(author).toFrontReference() },
{ files?.let { fileRepository.getHeadsByIds(files)
.map { it.toFrontReference() } } ?: listOf() }
).let { (author, files) ->
ChatMessageUI(
id = id ?: throw IllegalStateException("$this must be persisted"),
author = author,
message = message,
files = files,
forwardFrom = forwardFrom?.toFrontModel(),
replyTo = replyTo?.toFrontModel()
)
}
á¨áľ
fun <A, B> join(a: () -> A, b: () -> B) =
runBlocking(IO) {
awaitAll(async { a() }, async { b() })
}.let {
it[0] as A to it[1] as B
}
á ááľá ááłáĽ, áĽáá°áá á áááľ áľáአááááŁá áŞáŤáá˝á á áá áá, 200-400 ms áĽááááá, áá á áĽá á¨áá áĽáá áá ááľááá á áἠáá.
áĽáá° á áááłá°á áá ᣠáĽáá°áá ááááą áĽáŠ áľááŠááľ á¨áá ᣠáĽá á¨áá¨ááá áá á áŁá á¨áŤá áá-á á°ááłáłá áá áĽááľ á°á áááá˝ áĽáť á˛á°áŠ ᣠá¨áĽáŤááá˝ áĽááľ á á áááááśáš áá áááľáá ᣠáááááá á áľáአá áá¨áááá ᣠáľááá áĽá áá° áááá˝á ááááłá 4 s.
áĽáá°áá á áááľ á áááááľ áľá áá á¨á áá¤áľ 1300 áááááśá˝á áááľáŤáľ 1700-20 ms ááᢠáá á¨ááááŞáŤá áľáá ፠á¨á áá ááŁá áá, áá á ááá á˝ááŠá á áááłáá.
áľáአáá ááá˝á á ááŤá á á áááá˝á¨áśáľá°á ááá á áááááśá˝ áŁá˝ ááááŁá áŞáŤ áŤáá°áĄáľ? áááłá ᣠá á ááááš áá´áá˝ ááľáĽ á¨áĄáľá ááááŁá áŞáŤ áľáá ፠á ááááŠá áá°á á áá˝áá-
interface UserRemoteApi {
fun getUserById(id: UserReference): UserRemote
fun getUsersByIds(id: Set<UserReference>): Set<UserRemote> =
id.parallelStream()
.map { getUserById(it) }.collect(toSet())
fun getUsersByIds(id: List<UserReference>): List<UserRemote> =
id.parallelStream()
.map { getUserById(it) }.collect(toList())
}
á áááĽááľ áľáŞáśá˝ ááľáĽ á¨áĄáľá áá°áľá ááá¨áľ á°áľá áŤá°á¨á áá
áľááá áá°áŁáá˘
áááśá˝
- á áááááľ áá á¨á°áá°á¨á° áľáአáá°áľá á ááá á°ááĽáá˘
- áĽáŠ ááŹáľ.
Cons:
- á áľáአáá°ááŤáŠ á áááááśá˝ á¨áááᥠáĽáŤááá˝á á˛áŤáľá°áááľ á¨ááἠáááá á¨áá°áą á¨ááá¨áľ á áľáááááľá˘
- á áśáľá°á ááá á áááááśá˝ áá áááľ áá¨ááá˘
á¨á°ááŁáŤáááľ áá°á á¨áá ááľ á ááŤá¨áĽ áá á°ááłáłá áááá áá¨áľ ááťáá. á¨ááá˝á ááá¨áľ á¨áá˝ áĽáá á ááááŤáľ á¨á áááááľáá á ááťá¸á áĽá áá ááá¨áá á¨ááá áľáአá¨áĽáŤá áá´á áá áá á°áᢠááᢠá áĽá ááłá, á áááá á 2,5 áá á¨ááŻá, áá áá á ááá˝ á á á áá°áá.
áá¸ááŤ
áááŤá á áááááśá˝ á ááᤠááááľ áá¸á፠ááľá¨á áá˝áá ᣠáááľá ᣠá¨á°áá ááľá áááá˝ áĽáá°áá áĽááłáŤáá á á ááľ ááá áá ááľáĽ áŤá¨ááš (á ááľáĽ áá°áľ ááľáĽá á¨ááŽ)ᢠáĽáá°áá á áááľ áá¸ááŤáá˝á áĽáŤáľá ááľáŤáľ áá˝ááᣠáľááŞááá á áą @Cacheable áá áá áá˝ááᣠá á°á¨ááŞá áááá áĽáá° EhCache áŤá ááá á¨áá áá¸á፠á áĽá áá áá áá˝ááá˘
á¨á°ááá°á á˝áá áá¸ááŤáá˝ á áá á¨áááľ ááľá¨áŤáá˝ áŤáá¸á áĽáť ááᢠá áĽá áá᳠ᣠá á°áŤá˛á ááľá áá áŤá ááśá˝ á áŁá ááľáá¸á á¨áá°á áá (áĽáá á ᣠ50%) ᣠáá á áááá˝ áá ááá á áááľ ááśá˝ á áááŠáᢠáá á áŤááľ á ááłááľ ááťáťáŤáá˝á áŤáááŁáᣠááá áá á ááťá¸áá á áĽá á á ááááĽá (áĽá ááľáá´ áŤáľááááá)á˘
á¨á˘áá°áá´á˝á (á¨áĽá) áá¸ááŤáá˝ ááľáĽáľáĽ á¨áá á¨áá¸áľ á áááᎠáŤáľáááá¸ááᢠá á á áááᣠá áá áá á¨ááá áá¸á፠áá¸ááŤáá˝á á áá áá á¨á ááťá¸á á˝ááŽá˝á ááááłáľ á˛áááą á¨á°áťá ááááá˘
áááśá˝
- áŽáľ áłáááአáá¸ááŤáá áá°ááĽáŠá˘
- áááłáááľ áĽá áá á¨ááŻá (á á ááłááľ áááłáá˝).
Cons:
- á á°áłáłá° ááááľ áĽá á áá á¨áá á¨á áááá ááááľ áĽáľáá˘
- áľáá á¨ááľáłááľ á˝ááł á áá, á á°áá á¨á á áá¸ááŤáá˝ áá.
- ááľáĽáľáĽ ááľááśá˝áŁ áľá á°áśá˝ á áá°áľ áá áááŁááľ á áľá¸á᪠á¨áá á˝ááŽá˝á áŤáľá¨áľááá˘
á áŁá áĽá áá, áá¸ááŤáá˝ áĽá á áá á¨ááááľ á¨ááľá á˝ááŽá˝á á ááĽááľ ááá áá áĽáť áá. áá áááľ áá áĽá á áá ááá á¨ááŁá¸áá áááľ á áá°áá. ááá áĽáá áááá áĽááąá á áĽááá ááŤá áĽá á ááááŞáŤ á¨á°áááá á¨á áááá áá¤áľ ááááá áĽá á¨á፠áĽáť ááłá ááľá¨á á ááĽááľ.
á áĽá ááłáᣠáá¸ááŤáá˝ áá° 25% á áŤáŁá˘ á¨á ááťá¸á áá᪠áá°áŁáᢠá á°ááłáłá áá, áá¸ááŤáá˝ á áŁá áĽá ááłáśá˝ á áá¸á, áľááá áĽáá á áá áááŁá¸áá.
áá¤áśá˝
áľááá ᣠáŁá˝ ááááŁá áá á¨áá áááá á áááááľ á¨áá á á°ááŁá áá áĽá áĽáąá áááá á á ááłááľ ááá ááááśá˝á á°áááá°ááá˘
á¨áĽááá áá áá´áá˝ áááá á áááł áááááľ áá, á¨áĽáąá áĽá á áľá°áłá˝ áá¤áśá˝ á á.
á¨áĽááá áá´áá˝ á¨á°ááá° á˝áá áá á°á á áááá áá, á áááááľ á ááŹáśá˝ áá á ááááŤáľ. áľááá , áĽááá áááľááá˝ ááĽááľá á¨áááľáá á¨áá, á¨á áá áĽá ááá áá´áá˝á áááľ ááľáĽ ááľááŁáľ á°áᢠáá.
áááľááá˝á áááá á¨áá˝ááŁá¸á áááľ áá á á áŁáŤáá˝ á á-
- á¨ááἠáá áŤáá°ááłá°á áľáŤ (á¨ááŤáłáá ááἠáŤáľááááá, áľááá á áá á˝áá ááľáĽ á áá°áĽáŤáŤá);
- á¨á°ááłá°á áá°áľá á áá á á á¨áĄáľáá˝ ááľáááľá˘
á¨áĄáľáá˝ ááľáááľ á¨áá áĽáŞáá˝á ááĽá á áĽá
á ááááłá áĽá á á°ááłáłá áá áŽáąá áĽáá˛ááłá°á áŤá°áááá. á¨á˝áá ááŁá ááá á áá
áááľ áá áŤá°áŽá¨ ááááá˘
ááá: hab.com